

/*
mini change log:
0.14
added Argent Crusader's Tabard
added Jaina's Locket (untested)
added Boots of the Bay (untested)
now restarts archaeologybuddy directly. seems to work - means if hearthomatic bugs out, stopping and starting HB will now reset it (previously you had to close and restart HB completely)
new zone_id check allows for multiple pulses before giving up
flightor seems completly broken in dalaran. what looks like a new method of flying forward and upwards is causing the direction to get stuck turned on when a plugin calls the function. ive found what seems to be a suitable solution, however i noticed my character would crash into buildings every now and then, a real person might do that, so lets just call it a design feature ;)
replaced dalaran entry path .... again
ignored portal penalty when we're in the same zone as it's entry
adjustments to loosen waypoint tolerances

0.13
support for blacklists, but will ignore blacklist if all digsites are blacklisted
changed to using Flightor for all navigation, it seems a lot more reliable, and can work out how to get out of inn's, caves, ironforge, and other places whereas Navigator cant. a warning tho, it needs more testing!!!!
changes to navigation retries

0.12
dont even try to mount in tol barad (zone id 5389)
updated uldum portal opening quest
more changes to waiting for load - you're just gonna have to ignore the errors in the debug log, theyre non-critical and I cant seem to reliably get around them.
increased wrong_zone_re_check try count
removed a 200ms delay in pulse() that snuck in somehow
allowed hearthomatic to use some portals while still mounted
allowed hearthomatic to process the some portals in certain states without waiting for the next pulse
improvements to _land()
added dalaran portal exit path

0.11
fix for equipping and unequipping items when player has an empty slot in their inventory ... nude players... sheesh
increased waiting for load delay, new hb is showing non-critical errors every time it re-checks.
further tweaks to ironforge's exit path

0.10
new hyjal portal exit path
replaced the orgrimmar portal room exit path
added Tol Barad -> Orgrimmar Portal (untested)
added Teleport: Tol Barad (untested)
added Hellscream's Reach Tabard (untested)
fix astral recall functions preventing hearthstone from working

0.9
increased delay between stopping and restarting hb
astral recall changes (ty kaiser232)
added hyjal portal entry path (ty kaiser232)
added orgrimmar portal room exit path (ty sakue)
caching added for portal.ready(). the lua functions must have been making its stuff super slow
changed hyjal portal exit path

0.8
added teleport: moonglade
added teleport: tol barad (alliance)
stormwind -> tol barad added
tol barad -> stormwind added (interesting to note that its faster to teleport tol barad and take the stormwind portal than it is to teleport to stormwind directly)
deepholm -> therazane and therazane -> deepholm added
changes to help catch if player pulled agro while casting a portal
fix to help prevent repeated mounting & dismounting in tol barad. must be because archaeology buddy is confused about the zone its in
fixed bug with equipping old item after portal (reusing a WoWItem after a loading screen causes a critial bug in HB somewhere)
baradin's wardens tabard added (untested)
added penalties to some world portals to prevent silly loops

0.7
dalaran -> orgrimmar added
dalaran -> caverns of time added
changes to the use of land & dismount to make it more fluid
even more swimming detection, seems to work more reliably now
changes to stormwind -> blasted lands portal entry path

0.6 
changes to properly handle alliance / horde
most horde portals added
massive rewrite of portal choosing logic
now supports portal items (eg, potion of deepholm)
new hearthstone handling - website and database setup for automatic user-submittion
changed entry_path and exit_path method to use mount & flying (needs more testing)

0.5
identify available portals by their opening quest, or the characters level

0.4
fixed problem where bot would not land properly before using a portal. archbuddy's flying meant it was often dive bombing from a random height (usually non-lethal tho)
fixed some problems with the bot not waiting for loading screens
swimming means we've landed, stop trying to dive to the bottom of the ocean!

0.3
no longer needs to restart HB as often
fix problems with attempting to cast while moving (usually when sliding down a hill after dismounting)
will now retry casting portal if it failed
handles combat situations better
moved most of the logging output to debug

0.2
new entry portal logic for hard to reach places
blasted lands and twilight highlands entry portal pathing added

0.1
initial release 


todo: horde deepholm portal
todo: work out why honorbuddy mounts / dismounts a lot at tol barad
todo: undercity exit path
todo: re-do dalarn exit path

*/


namespace archaeologybuddy_hearthomatic
{
    using Styx.Logic;
    using System;
    using Styx.Helpers;
    using Styx.Logic.Pathing;
    using System.Threading;
    using System.Diagnostics;
    using Styx.WoWInternals;
    using Styx.WoWInternals.WoWObjects;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Xml.Linq;
    using System.Net;
    using Styx.Plugins.PluginClass;
    using Styx;

    using Bots.ArchaeologyBuddy;

    using Styx.Logic.Combat;//WoWSpell

    using System.Net.Mail;//to send email

   



    public class archaeologybuddy_hearthomatic : HBPlugin
    {
        public override string Name { get { return "archaeologybuddy hearthomatic"; } }
        public override string Author { get { return "any"; } }
        public override Version Version { get { return new Version(0, 14); } }
        public override bool WantButton { get { return false; } }
        public override string ButtonText { get { return "hearthomatic"; } }

        private bool initialized = false;

        private static readonly LocalPlayer Me = ObjectManager.Me;
        private Digsite _last_checked_digsite = null;
        private float _archaeologybuddy_hearthomatic_distance_threshold = 1000;//a portal route must be X better than archbuddy's, otherwise its not worth the effort

        List<_hearthomatic_port_location> port_locations;
        
        int hearthomatic_status = 0;
        static int _hearthomatic_status_NONE = 0;
        static int _hearthomatic_status_STEP1 = 1;
        static int _hearthomatic_status_STEP2 = 2;
        static int _hearthomatic_status_STEP3 = 3;

        int shortest_distance_portal_path_index = 0;
        List<_hearthomatic_port_location> shortest_distance_portal_path;

        int skip_pulse = 0;//used to skip a pulse or two after each load (HB takes a while to update all its details)

        Random _random = new Random();

        bool _submit_hearthstone = false;

        int hearthomatic_wrong_zone_re_check = 0;
 
        public override void Initialize()
        {
            if (Styx.BotManager.Current.Name != "ArchaeologyBuddy")
            {   return;
            }

            Log("hearthomatic v{0} loading", Version.ToString());

            ObjectManager.Update();

            if(Me.Class == Styx.Combat.CombatRoutine.WoWClass.Mage)
            {   Log("you are a mage! mages rock! you rock!");
            }

            //get all the portals we can use
            port_locations = _get_available_portals(Me);


            //some debugging
            DebugLog("Me.MapName = {0}", Me.MapName);
            string[] t = Lua.GetReturnValues("local t = {}; " +
                                                    "SetMapToCurrentZone(); " +
                                                    "SetMapZoom(GetCurrentMapContinent()); " +
                                                    "table.insert(t, GetCurrentMapAreaID()); " +
                                                    "for i=1,GetNumMapLandmarks() do " +
                                                        "name, _, textureIndex, x, y = GetMapLandmarkInfo(i); " +
                                                        "if (textureIndex == 177) then " +
                                                            "table.insert(t, name); " +
                                                            "table.insert(t, x); " +
                                                            "table.insert(t, y); " +
                                                        "end " +
                                                    "end " +
                                                    "return unpack(t); ").ToArray();
            uint current_map_id = uint.Parse(t[0]);
            DebugLog("current_map_id = {0}", current_map_id);
            DebugLog("Me.ZoneId = {0}", Me.ZoneId);
            DebugLog("Me.Location = {0}", Me.Location.ToString());
            DebugLog("Me.HearthstoneAreaId = {0}", Me.HearthstoneAreaId);

            int portal_count = 0;//just some reporting to the user 

            foreach (_hearthomatic_port_location _portal in port_locations)
            {
                Log("portal {0}. ready={1}", _portal.name, _portal.ready());
                portal_count++;
            }

            BotEvents.OnBotStart += startBot;
            BotEvents.OnBotStop += stopBot;

            hearthomatic_status = _hearthomatic_status_NONE;

            Log("ready. with access to {0} portals", portal_count);

            initialized = true;

            //for testing
            /*BotEvents.OnBotStart -= startBot;
            BotEvents.OnBotStop -= stopBot;
            hearthomatic_status = _hearthomatic_status_STEP1;
            shortest_distance_portal_path_index = 0;/**/
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Ironforge") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Dalaran"), port_locations.Find(item => item.name == "Dalaran -> Caverns of Time") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Undercity") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Stormwind -> Blasted Lands Portal") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Stormwind"), port_locations.Find(item => item.name == "Stormwind -> Blasted Lands Portal") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Moonglade") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Tol Barad"), port_locations.Find(item => item.name == "Tol Barad -> Stormwind Portal"), port_locations.Find(item => item.name == "Stormwind -> Uldum Portal") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Wrap of Unity (Stormwind)") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Hyjal -> Stormwind Portal") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Stormwind -> Hyjal Portal") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Orgrimmar") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Baradin's Wardens Tabard") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Tol Barad"), port_locations.Find(item => item.name == "Tol Barad -> Stormwind Portal") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Dalaran -> Caverns of Time") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Argent Crusader's Tabard") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Stormwind -> Uldum Portal") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> {port_locations.Find(item => item.name == "Stormwind -> Twilight Highlands Portal") };
            //shortest_distance_portal_path = new List<_hearthomatic_port_location> { port_locations.Find(item => item.name == "Teleport: Tol Barad"), port_locations.Find(item => item.name == "Tol Barad -> Stormwind Portal"), port_locations.Find(item => item.name == "Stormwind -> Hyjal Portal") };


            //insane traveller testing mode - Achievement Unlocked!
            // take as many portals as possible
            //  not taken: 
            //      Dalaran -> Stormwind Portal
            //      Teleport: Theramore
            //      Potion of Deepholm
            /*_hearthomatic_port_location stormwind_tabard = port_locations.Find(item => item.name == "Wrap of Unity (Stormwind)" && item.ready());
            if (stormwind_tabard == null)
            {   stormwind_tabard = port_locations.Find(item => item.name == "Teleport: Stormwind");
            }
            _hearthomatic_port_location tolbarad_tabard = port_locations.Find(item => item.name == "Baradin's Wardens Tabard" && item.ready());
            if (stormwind_tabard == null)
            {   tolbarad_tabard = port_locations.Find(item => item.name == "Teleport: Tol Barad");
            }
            shortest_distance_portal_path = new List<_hearthomatic_port_location> { 
                
                tolbarad_tabard, 
                port_locations.Find(item => item.name == "Tol Barad -> Stormwind Portal"),

                port_locations.Find(item => item.name == "Stormwind -> Twilight Highlands Portal"), 
                port_locations.Find(item => item.name == "Twilight Highlands -> Stormwind Portal"), 

                port_locations.Find(item => item.name == "Stormwind -> Deepholm Portal"), 
                port_locations.Find(item => item.name == "Deepholm -> Stormwind Portal"), 

                port_locations.Find(item => item.name == "Stormwind -> Hyjal Portal"),
                port_locations.Find(item => item.name == "Hyjal -> Stormwind Portal"), 

                port_locations.Find(item => item.name == "Stormwind -> Blasted Lands Portal"), 
                port_locations.Find(item => item.name == "Teleport: Shattrath"), 
                port_locations.Find(item => item.name == "Shattrath -> Stormwind"), 

                port_locations.Find(item => item.name.Contains("Hearthstone")), 
                stormwind_tabard,

                port_locations.Find(item => item.name == "Teleport: Dalaran"),
                port_locations.Find(item => item.name == "Dalaran -> Caverns of Time"),
                port_locations.Find(item => item.name == "Teleport: Stormwind"),

                port_locations.Find(item => item.name == "Stormwind -> Uldum Portal"), 
                port_locations.Find(item => item.name == "Teleport: Ironforge"),
            };*/




            //20120115 - none of the below works.
            //experimenting with ways to convert hearthstone area id into useable coordinates

            /*WoWDb.DbTable area_table = StyxWoW.Db[Styx.Patchables.ClientDb.AreaTable];
            WoWDb.Row area_row = area_table.GetRow(Me.HearthstoneAreaId);
            int area_row_id = area_row.GetField<int>(0);
            int area_row_zone_id = area_row.GetField<int>(2);
            string area_row_name = area_row.GetField<string>(11);
            Log("db returned: Me.HearthstoneAreaId={0}, row_id={1}, zone_id={2}, name={3}", Me.HearthstoneAreaId, area_row_id, area_row_zone_id, area_row_name);

            uint temp = 0;
            while (temp <= 25)
            {
                switch(temp)
                {
                    case 11:
                        string temp_string = area_row.GetField<string>(temp);
                        Log(" field {0} = {1}", temp, temp_string);
                        break;
                    default:
                        int temp_int = area_row.GetField<int>(temp);
                        Log(" field {0} = {1}", temp, temp_int);
                        break;
                }
                temp++;
            }

            Log("-------------------------");
            */


            /*
            WoWDb.DbTable map_table = StyxWoW.Db[Styx.Patchables.ClientDb.WorldMapArea];

            Log("map_table.NumRows = {0}", map_table.NumRows);


            //System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\WorldMapArea.txt");

            Log("map_table.MaxIndex={0}", map_table.MaxIndex);

            uint temp2 = 0;//MinIndex is int, getRow requires uint. i dont trust it
            while (temp2 <= map_table.MaxIndex)
            {
                WoWDb.Row map_row = map_table.GetRow(temp2);
                if (map_row != null)
                {
                    int id = map_row.GetField<int>(2);
                    if (id == area_row_id || id == area_row_zone_id)
                    {
                        //Log("row {0}:", temp2);
                        temp = 0;
                        while (map_row != null && temp <= 64)
                        {
                            int temp_int = map_row.GetField<int>(temp);
                            float temp_float = map_row.GetField<float>(temp);
                            string temp_string = map_row.GetField<string>(temp);
                        
                            string log = string.Format(" row({0}) field({1}) - int={2} float={3} string={4}", temp2, temp, temp_int, temp_float, temp_string);

                            Log(log);
                            //file.WriteLine(log);

                            temp++;
                        }
                    }                    

                    //Log("row({0}) - name={1}", temp2, map_row.GetField<string>(3));
                    //file.WriteLine(string.Format("row({0}) - name={1}", temp2, map_row.GetField<string>(3)));
                }

                temp2++;
            }

            //file.Close();

            Log("-------------------------");
            */
            /*
            WoWPoint worldPoint = new WoWPoint();
            
            //WoWDb.DbTable worldMapArea = StyxWoW.Db[ClientDb.WorldMapArea];
            WoWDb.DbTable worldMapArea = StyxWoW.Db[Styx.Patchables.ClientDb.WorldMapArea];

            WoWDb.Row worldMapAreaFields = worldMapArea.GetRow(mapId);
            float ay = worldMapAreaFields.GetField<float>(4);
            float by = worldMapAreaFields.GetField<float>(5);
            float ax = worldMapAreaFields.GetField<float>(6);
            float bx = worldMapAreaFields.GetField<float>(7);
            worldPoint.X = ax + (Y * (bx - ax));
            worldPoint.Y = ay + (X * (by - ay));
      
            float _height = 0;
            Styx.Logic.Pathing.Navigator.FindHeight(worldPoint.X, worldPoint.Y, out _height);
            worldPoint.Z = _height;

            return worldPoint;
            */
            
            
        }

        private void startBot(EventArgs args)
        {
            DebugLog("startBot");

            hearthomatic_status = _hearthomatic_status_NONE;
        }
        private void stopBot(EventArgs args)
        {
            DebugLog("stopBot");

            hearthomatic_status = _hearthomatic_status_NONE;
        }
        
        public override void Pulse()
        {
            
            //test new exit paths:
            //ironforge exitpath:
            //WoWPoint[] exit_path = new WoWPoint[] { new WoWPoint(-4721.52, -909.597, 508.7111), new WoWPoint(-4897.31, -900.8706, 510.111), new WoWPoint(-4930.193, -852.3321, 508.9218), new WoWPoint(-4984.761, -879.2337, 508.1972), new WoWPoint(-5043.228, -809.2598, 506.1768)};
            //stormwind exitpath:
            //WoWPoint[] exit_path = new WoWPoint[] { new WoWPoint(-9018.447, 887.8731, 29.6206), new WoWPoint(-9011.675, 867.67, 147.2538), new WoWPoint(-9004.104, 868.0667, 142.9046), new WoWPoint(-9003.69, 876.8965, 138.0133), new WoWPoint(-9012.8, 875.5212, 133.1509), new WoWPoint(-8999.803, 862.4565, 129.4713) };
            //hyjal horde entry path:
            //WoWPoint[] exit_path = new WoWPoint[] { new WoWPoint(5567.691, -3774.967, 1653.331), new WoWPoint(5541.264, -3712.648, 1599.609), new WoWPoint(5547.796, -3643.636, 1566.074), new WoWPoint(5503.812, -3624.667, 1567.542) };
            //orgrimmar portal exit path
            /*WoWPoint[] exit_path = new WoWPoint[] { 
                new WoWPoint(1771.753, -4328.003, 17.72168), 
                new WoWPoint(1728.147, -4280.142, 23.55666), 
                new WoWPoint(1725.075, -4272.879, 24.31613), 
                new WoWPoint(1710.142, -4289.579, 32.15226),
                new WoWPoint(1711.576, -4324.221, 33.34835)
            };
            _follow_WoWPoint_array(exit_path);
            return;*/

            
            if (!initialized || Styx.BotManager.Current.Name != "ArchaeologyBuddy")// i wanted to avoid a string comparison every pulse
            {   return;
            }

            //do nothing if we're in combat
            if (StyxWoW.Me.Combat || StyxWoW.Me.IsCasting)
            {   return;
            }

            if (skip_pulse > 0)
            {
                DebugLog("Skip Pulse");
                skip_pulse--;
                return;
            }

            ObjectManager.Update();

            Digsite digsite = Bots.ArchaeologyBuddy.ArchBuddy.CurrentDigsite;

            //something has gone wrong, these shouldnt happen. maybe wait for the next pulse
            if (digsite == null || digsite.Name == null || Me.ZoneId == null || Me.ZoneId <= 0 || !StyxWoW.IsInGame || StyxWoW.Me.Dead)
            {   return;
            }

            if (_submit_hearthstone)
            {
                _submit_hearthstone = false;
                _submit_hearthstone_details();
                return;
            }

            if (hearthomatic_status > _hearthomatic_status_NONE)
            {
                _process_status();
                return;
            }
            else if (digsite != _last_checked_digsite)
            {
                _last_checked_digsite = digsite;

                float distance = Me.Location.Distance2D(digsite.Location);

                DebugLog("checking if {0} is really the best digsite ({1} yards)", digsite.Name, distance);

                if (Me.ZoneId == digsite.ZoneId)
                {
                    DebugLog("selected digsite is in the same zone as us ({0}), aborting check", Me.ZoneId);
                }
                else
                {
                    //found some lua stuff in some old archbuddy code that gets a list of digsites
                    ObjectManager.Update();
                    Thread.Sleep(100);
                    string[] t = Lua.GetReturnValues("local t = {}; " +
                                                    "SetMapToCurrentZone(); " +
                                                    "SetMapZoom(GetCurrentMapContinent()); " +
                                                    "table.insert(t, GetCurrentMapAreaID()); " +
                                                    "for i=1,GetNumMapLandmarks() do " +
                                                        "name, _, textureIndex, x, y = GetMapLandmarkInfo(i); " +
                                                        "if (textureIndex == 177) then " +
                                                            "table.insert(t, name); " +
                                                            "table.insert(t, x); " +
                                                            "table.insert(t, y); " +
                                                        "end " +
                                                    "end " +
                                                    "return unpack(t); ").ToArray();
                    uint current_map_id = uint.Parse(t[0]);

                    string best_digsite = digsite.Name;
                    float best_digsite_distance = Me.Location.Distance2D(digsite.Location);
                    float best_digsite_distance_real = Me.Location.Distance2D(digsite.Location);//display purposes
                    List<_hearthomatic_port_location> best_digsite_path = new List<_hearthomatic_port_location>();


                    //check if all digsites are blacklisted, if they are ignore the list
                    bool use_blacklist = true;
                    int digsite_count = 0;
                    int blacklist_count = 0;
                    for (int i = 1; i < t.Length; i += 3)
                    {
                        digsite_count++;
                        foreach (string blacklisted_digsite in Bots.ArchaeologyBuddy.ArchBuddy.BlacklistedDigsites)
                        {
                            if (t[i].Equals(blacklisted_digsite))
                            {
                                blacklist_count++;
                                break;
                            }
                        }
                    }
                    if (blacklist_count == digsite_count)
                    {
                        Log("Error - All digsites are blacklisted. Temporarily ignoring blacklist.");
                        use_blacklist = false;
                    }


                    //check the digsites
                    for (int i = 1; i < t.Length; i += 3)
                    {
                        WoWPoint p = _MapToWorldCoords(float.Parse(t[i + 1]), float.Parse(t[i + 2]), current_map_id);

                        DebugLog("comparing other options for the digsite {0} ({1} yards)", t[i], p.Distance2D(Me.Location));

                        
                        //check if this digsite is blacklisted. if so, dont consider it at all
                        if (use_blacklist)
                        {
                            bool blacklisted = false;//wtf, c# cant continue from a nested loop
                            foreach (string blacklisted_digsite in Bots.ArchaeologyBuddy.ArchBuddy.BlacklistedDigsites)
                            {
                                if (t[i].Equals(blacklisted_digsite))
                                {
                                    Log("{0} is blacklisted. Ignoring.", blacklisted_digsite);
                                    blacklisted = true;
                                    break;
                                }
                            }
                            if (blacklisted)
                            {
                                continue;
                            }
                        }


                        List<_hearthomatic_port_location> portal_path = new List<_hearthomatic_port_location>();
                        List<_hearthomatic_port_location> best_portal_path = new List<_hearthomatic_port_location>();
                        _find_portal_path(4, current_map_id, Me.Location, p, port_locations, portal_path, ref best_portal_path);

                        float distance2 = _portal_path_get_distance(Me.Location, p, best_portal_path);
                        if (
                            distance2 < best_digsite_distance &&         //if we found a new better option
                            (
                                (
                                    best_digsite_path.Count == 0 &&         //if we're to replace AB's digsite make sure its better than it by at least ___ yards
                                    distance2 < best_digsite_distance - _archaeologybuddy_hearthomatic_distance_threshold
                                )
                                ||
                                (
                                    best_digsite_path.Count > 0     //if we've already replaced it, and have now found an even better option, take it!
                                )
                            )
                        )
                        {
                            DebugLog("digsite {0} ({1} yards) is a better option. portal path: {2}", t[i], distance2, _portal_path_to_string(Me.Location, p, best_portal_path));

                            best_digsite = t[i];
                            best_digsite_distance = distance2;
                            best_digsite_distance_real = _portal_path_get_distance(Me.Location, p, best_portal_path, false);
                            best_digsite_path = best_portal_path.ToList();
                        }
                    }

                    if (best_digsite_path.Count > 0 && best_digsite_distance_real < distance)//double check the distance again just in case a negative penalty caused us to choose incorrectly
                    {
                        Log("hearthomatic has chose a better digsite - {0} ({1} yards) vs {2} ({3} yards)", best_digsite, best_digsite_distance_real, digsite.Name, distance);
                        Log("  portal path: {0}", _portal_path_to_string(Me.Location, Me.Location, best_digsite_path));

                        hearthomatic_status = _hearthomatic_status_STEP1;
                        shortest_distance_portal_path = best_digsite_path.ToList();
                        shortest_distance_portal_path_index = 0;

                        _process_status();
                    }
                }
            }
        }//end of pulse

        

        private void _process_status()
        {
            DebugLog("_process_status()");

            ObjectManager.Update();

            //just in case we're still zoing through a portal.
            _wait_for_load();

            //just in case we pulled agro.
            if (StyxWoW.Me.Combat || StyxWoW.Me.IsCasting)
            {
                 DebugLog("we're in combat, aborting");
                 return;
            }

            if (hearthomatic_status > _hearthomatic_status_NONE && shortest_distance_portal_path.Count > 0)
            {
                _hearthomatic_port_location portal = shortest_distance_portal_path[shortest_distance_portal_path_index];


                if (hearthomatic_status == _hearthomatic_status_STEP1)//move to land, and dismount, then let HB take over for a second, in case we get pulled into combat
                {
                    if (portal.entry_continent_id > 0)
                    {
                        while (Me.Location.Distance(portal.entry_location) > 6)
                        {
                            DebugLog("flying to {0}", portal.name);
                            _flyto(portal.entry_location, 6, false);
                            Thread.Sleep(200);
                        }
                    }

                    if (Flightor.MountHelper.Mounted && portal.entry_path == null)//dont land if we have an entry path
                    {
                        DebugLog(string.Format("landing so we can port"));
                        _land();
                        //DebugLog(string.Format("dismount"));
                        //_dismount();//dont dismount yet, otherwise we dismount, mount, dismount, portal. looks super fake
                    }

                    hearthomatic_status = _hearthomatic_status_STEP2;

                    if (!portal.needs_pulse(_hearthomatic_status_STEP1))
                    {
                        _process_status();
                    }

                    return;                                     
                }
                else if (hearthomatic_status == _hearthomatic_status_STEP2)//use the portal
                {
                    WoWMovement.MoveStop();

                    //land and dismount twice (step1 and 2) because HB -WILL- try to mount up again and resume its stuff between steps.
                    //  necessary evil, so that HB can handle if we happened to pull agro between step1 and step2
                    if (portal.entry_continent_id > 0)
                    {
                        while (Me.Location.Distance(portal.entry_location) > 6)
                        {
                            DebugLog("flying to {0}", portal.name);
                            _flyto(portal.entry_location, 6, false);
                            Thread.Sleep(200);
                        }
                    }

                    _portal_check_entry_path(portal);

                    if (Flightor.MountHelper.Mounted)
                    {
                        DebugLog(string.Format("landing so we can port"));
                        _land();
                        if (portal.needs_dismount())
                        {
                            DebugLog(string.Format("dismount"));
                            _dismount();
                        }
                    }

                    WoWMovement.MoveStop();
                    
                    uint result = portal.use();

                    if(result == _hearthomatic_port_location.use_result_FAILCOMBAT)
                    {   
                        return;
                    }
                    else if (result == _hearthomatic_port_location.use_result_SUCCESS)
                    {
                        hearthomatic_status = _hearthomatic_status_STEP3;

                        if (!portal.needs_pulse(_hearthomatic_status_STEP2))
                        {
                            _process_status();
                        }

                        //skip_pulse = 1;//20120130 - testing with. by adding a skip here AB should take off and fly away before changing direction a few seconds later (should look more natural)
                                         //20120212 - removed because this step is used for intermediary portals now too
                        return;
                    }
                    else
                    {
                        Log("something went wrong with the portal! cancel everything!");
                        hearthomatic_status = _hearthomatic_status_NONE;

                        return;
                    }
                }
                else if (hearthomatic_status == _hearthomatic_status_STEP3) //do post-portal stuff
                {
                    //check we ended up where we expected to end up
                    if (hearthomatic_wrong_zone_re_check <= 0)
                    {
                        hearthomatic_wrong_zone_re_check = 3;
                    }
                    while (Me.ZoneId != portal.exit_zone_id)
                    {
                        //sometimes HB doesnt update Me.ZoneId quickly enough
                        Log("something went wrong with the portal {0}, we are in the zone id ({1}) but we expected to be in ({2})", portal.name, Me.ZoneId, portal.exit_zone_id);
                        hearthomatic_wrong_zone_re_check--;

                        if (hearthomatic_wrong_zone_re_check <= 0)
                        {
                            Log("giving up! we are in the wrong zone id - cancel everything!");
                            hearthomatic_status = _hearthomatic_status_NONE;
                            return;
                        }
                        else
                        {
                            DebugLog("wrong zone id!? waiting for HB to update, its probably just old data");
                            skip_pulse++;
                            return;
                        }
                    }

                    _portal_check_exit_path(portal);

                    //success! go on to the next portal
                    hearthomatic_status = _hearthomatic_status_STEP1;
                    shortest_distance_portal_path_index++;

                    //if we're NOT done with the shortest_distance_portal_path list of portals to take
                    if (shortest_distance_portal_path_index < shortest_distance_portal_path.Count)
                    {
                        //  then maybe we want to skip the pulse and right on to the next portal immediately
                        if (!portal.needs_pulse(_hearthomatic_status_STEP3))
                        {
                            _process_status();
                        }

                        return;//needed because the above _process_status may change the shortest_distance_portal_path_index, without it restart could potentially be called twice
                    }
                }
            }
            else
            {
                Log("error unknown status. something impossible went wrong?! this should never happen");
                hearthomatic_status = _hearthomatic_status_NONE;
            }


            //if we're done with the shortest_distance_portal_path list of portals to take we should restart HB
            if(shortest_distance_portal_path_index >= shortest_distance_portal_path.Count)
            {
                //reset everything
                hearthomatic_status = _hearthomatic_status_NONE;

                _restart_hb();//forces archaeology buddy to choose a new target
            }


            return;
        }


        private void _portal_check_exit_path(_hearthomatic_port_location portal)
        {
            //sometimes we need to take an escape path after using an intermediary portal
            //  to get out of the inn for example
            if (portal.exit_path != null && portal.exit_path.Length > 0)
            {
                DebugLog("portal {0} has an exit path, taking it", portal.name);
                _follow_WoWPoint_array(portal.exit_path);
            }
            return;
        }
        private void _portal_check_entry_path(_hearthomatic_port_location portal)
        {
            //sometimes we need to take an escape path after using an intermediary portal
            //  to get out of the inn for example
            if (portal.entry_path != null && portal.entry_path.Length > 0)
            {
                DebugLog("portal {0} has an entry path, taking it", portal.name);
                _follow_WoWPoint_array(portal.entry_path);
            }
            return;
        }

        private void _follow_WoWPoint_array(WoWPoint[] path)
        {
            for (int i = 0; i < path.Length; i++)
            {
                if (i == path.Length - 1)
                {
                    _flyto(path[i], 3, false);//stricter tolerance for the last portal in the waypoint path
                }
                else
                {
                    _flyto(path[i], 6, false);//6 yard tolerance instead
                }
            }
        }
        

        private void _restart_hb()
        {
            /*Thread helper = new Thread(
                unused => _restart_hb_worker()
            );
            helper.Start();*/

            Log("Restarting ArchBuddy....");
            Bots.ArchaeologyBuddy.ArchBuddy.Instance.Stop();
            Bots.ArchaeologyBuddy.ArchBuddy.Instance.Start();

            return;
        }
        private void _restart_hb_worker()
        {
            Log("stopping honorbuddy");
            Thread.Sleep(100);
            Styx.Logic.BehaviorTree.TreeRoot.Stop();
            do
            {
                DebugLog("waiting for hb to stop");
                Thread.Sleep(250);
            }
            while (Styx.Logic.BehaviorTree.TreeRoot.IsRunning);
            Thread.Sleep(1000);
            Styx.Logic.BehaviorTree.TreeRoot.Start();
            Thread.Sleep(250);
            Log("honorbuddy has been restarted by hearthomatic");
            Thread.Sleep(100);
            Thread.CurrentThread.Abort();
        }

        private static void _wait_for_load()
        {
            while (!StyxWoW.IsInGame || StyxWoW.Me.Dead)
            {
                DebugLog("waiting for load");
                Thread.Sleep(2000);

                StyxWoW.SleepForLagDuration();
                Thread.Sleep(100);
                ObjectManager.Update();
            }
            return;
        }


        #region Log functions
        private static void Log(bool isDebug, string format, params object[] args)
		{
			if (isDebug)
			{	Logging.WriteDebug("[hearthomatic]: {0}", string.Format(format, args));
			}
			else
			{	Logging.Write("[hearthomatic]: {0}", string.Format(format, args));
			}
		}
		private static void Log(string format, params object[] args)
		{	
            Log(false, format, args);
            Log(true, format, args);
		}
        private static void Log(string message)
        {  
            Log(false, message);
            Log(true, message);
        }
        private static void DebugLog(string format, params object[] args)
        {   Log(true, format, args);
        }
        private static void DebugLog(string message)
        {   Log(true, message);
        }
        #endregion


        private static void _land()
        {
            float _height = 0;
            float _last_height = 0;
            int tries = 2;

            MirrorTimerInfo breath = Me.GetMirrorTimerInfo(Styx.MirrorTimerType.Breath);//i found isswimming to be unreliable
            
            if (Me.IsSwimming || breath.IsVisible)//we're swimming. stop tring to dive to the bottom of the ocean! she canno take the pressure capt'n
            {
                return;
            }

            WoWMovement.MoveStop();

            Styx.Logic.Pathing.Navigator.FindHeight(Me.Location.X, Me.Location.Y, out _height);//turns out this is pretty inaccurate when it comes to buildings n stuff

            while (Me.IsFlying && !(Me.IsSwimming || breath.IsVisible))//Math.Abs(Me.Location.Z - _height) > 2
            {
                _last_height = Me.Location.Z;

                WoWMovement.Move(WoWMovement.MovementDirection.Descend);

                StyxWoW.SleepForLagDuration();
                Thread.Sleep(250);
                ObjectManager.Update();

                //Log("still have {0} to descend", Math.Abs(Me.Location.Z - _height));

                if (_last_height == Me.Location.Z)//we dont appear to be moving
                {
                    if (tries <= 0)
                    {
                        DebugLog("still have {0} to descend but we dont appear to be moving (perhaps a building or a tree)", Math.Abs(Me.Location.Z - _height));

                        WoWMovement.MoveStop();
                        return;
                    }
                    else
                    {
                        DebugLog("_land() - we're not moving. retrying ({0})", tries);//if we dont retry we can end up dismounting from really high up
                        tries--;
                    }
                }
                else
                {
                    tries = 2;
                }
            }

            WoWMovement.MoveStop();
            return;
        }

        private static void _dismount()
        {
            WoWMovement.MoveStop();

            Flightor.MountHelper.Dismount();

            StyxWoW.SleepForLagDuration();
            Thread.Sleep(100);
            ObjectManager.Update();

            WoWPoint temp_location;
            do
            {
                temp_location = Me.Location;
                Thread.Sleep(200);
                ObjectManager.Update();
            }
            while (Me.Location != temp_location);

            return;
        }

        private static bool check_zone_id_can_mount(uint zoneid)
        {
            switch (zoneid)
            {
                case 5389://dont mount in tol barad
                    return false;
                default:
                    return true;                  
            }
        }

        private static void _flyto(WoWPoint loc)
        {
            //_flyto(loc, 1, true);
            _flyto(loc, 3, true);
        }
        private static void _flyto(WoWPoint loc, int tolerance, bool land)
        {
            float _last_check = 0;

            DebugLog("_flyto: {0}", loc.ToString());

            WoWMovement.MoveStop();

            ObjectManager.Update();
            if (Flightor.MountHelper.CanMount && !Flightor.MountHelper.Mounted && check_zone_id_can_mount(Me.ZoneId))
            {
                Flightor.MountHelper.MountUp();
                StyxWoW.SleepForLagDuration();
                Thread.Sleep(250);
                while (Me.IsCasting) { Thread.Sleep(250); }
                StyxWoW.SleepForLagDuration();
                Thread.Sleep(250);
            }

            int tries = 4;
            //int tries = 5;
            bool dalaran_move_stop_used = false;
            while (Me.Location.Distance(loc) > tolerance && tries > 0)//while we arent there yet
            {                
                _last_check = Me.Location.Distance(loc);

                //DebugLog("_flyto (1) flying to {0} (tolerance:{1},triesleft:{2})", loc.ToString(),tolerance,tries);
                DebugLog("_flyto (1) flying to {0}", loc.ToString());
                /*if (false && Me.ZoneId == 4395 && Flightor.MountHelper.Mounted && !Me.IsIndoors && Me.IsFlying && tries > 2)//only click to move for 1st (=5) and 2nd (=4) and 3rd (=3) attempt. flighttor is buggy, but somtimes gets us there ... eventually
                {
                    DebugLog("Dalaran outdoors - using clicktomove instead");

                    if (!dalaran_move_stop_used)
                    {
                        WoWMovement.MoveStop();
                        dalaran_move_stop_used = true;
                    }
                    Styx.WoWInternals.WoWMovement.ClickToMove(loc);
                    Thread.Sleep(750);
                }
                else*/
                {
                    Flightor.MoveTo(loc);
                }

                StyxWoW.SleepForLagDuration();
                Thread.Sleep(250);

                Styx.WoWInternals.WoWMovement.MoveStop(Styx.WoWInternals.WoWMovement.MovementDirection.JumpAscend);
                Styx.WoWInternals.WoWMovement.MoveStop(Styx.WoWInternals.WoWMovement.MovementDirection.Forward);
            
                if (Me.Location.Distance(loc)  == _last_check)
                {
                    DebugLog("_flyto (1) we dont appear to be moving");
                    StyxWoW.SleepForLagDuration();
                    Thread.Sleep(250);
                    tries--;
                }
            }

            if (land)
            {
                if (Flightor.MountHelper.Mounted)
                {
                    DebugLog(string.Format("_flyto (1) landing"));
                    _land();
                    //DebugLog(string.Format("_flyto (1) dismount"));//looks more natural without the dismount
                    //_dismount();
                }

                tries = 3;

                while (Me.Location.Distance2D(loc) > 1 && tries > 0)
                {
                    _last_check = Me.Location.Distance(loc);

                    DebugLog("_flyto (2) moving to {0}", loc.ToString());
                    Navigator.MoveTo(loc);
                    StyxWoW.SleepForLagDuration();
                    Thread.Sleep(250);

                    if (Me.Location.Distance(loc) == _last_check)
                    {
                        DebugLog("_flyto (2) we dont appear to be moving");
                        tries--;
                    }
                }
            }

            return;
        }

        

        //convert map coords to HB world coordinates
        //found in old archbuddy source code. heavily modified
        private WoWPoint _MapToWorldCoords(float X, float Y, uint mapId)
        {
            WoWPoint worldPoint = new WoWPoint();
            
            //WoWDb.DbTable worldMapArea = StyxWoW.Db[ClientDb.WorldMapArea];
            WoWDb.DbTable worldMapArea = StyxWoW.Db[Styx.Patchables.ClientDb.WorldMapArea];

            WoWDb.Row worldMapAreaFields = worldMapArea.GetRow(mapId);
            float ay = worldMapAreaFields.GetField<float>(4);
            float by = worldMapAreaFields.GetField<float>(5);
            float ax = worldMapAreaFields.GetField<float>(6);
            float bx = worldMapAreaFields.GetField<float>(7);
            worldPoint.X = ax + (Y * (bx - ax));
            worldPoint.Y = ay + (X * (by - ay));
      
            float _height = 0;
            Styx.Logic.Pathing.Navigator.FindHeight(worldPoint.X, worldPoint.Y, out _height);
            worldPoint.Z = _height;

            return worldPoint;
        }



        //equip item borrowed from autoequip plugin
        #region Equip Item
        private static void EquipItem(int bagIndex, int bagSlot, int targetSlot)
        {
            Lua.DoString(
                "ClearCursor(); PickupContainerItem({0}, {1}); EquipCursorItem({2}); if StaticPopup1Button1 and StaticPopup1Button1:IsVisible() then StaticPopup1Button1:Click(); end;",
                bagIndex + 1, bagSlot + 1, targetSlot);
        }
        private static void EquipItemIntoSlot(WoWItem item, Styx.Logic.Inventory.InventorySlot slot)
        {
            EquipItem(item.BagIndex, item.BagSlot, (int)slot);
        }
        private static void EquipItem(WoWItem item)
        {
            EquipItem(item.BagIndex, item.BagSlot, (int)item.ItemInfo.EquipSlot);
        }
        #endregion
        #region Unequip Item
        private static void UnequipSlot(int slot)
        {
            //doh, had to write my own macro
            //for each bag
            //  get number of slots
            //  for each slot
            //    if we cant pickup anything (its an empty slot)
            //      pickup item in inventory {0} 
            //      and place it in that empty container slot
            //      return
            Lua.DoString("ClearCursor(); for i=0,4 do for j=1,(GetContainerNumSlots(i)) do if not GetContainerItemLink(i,j) then PickupInventoryItem({0}); PickupContainerItem(i,j); return; end; end; end;", slot);
        }
        private static void UnequipSlot(Styx.Logic.Inventory.InventorySlot slot)
        {
            UnequipSlot((int)slot);
        }
        private static void UnequipItem(WoWItem item)
        {
            UnequipSlot((int)item.ItemInfo.EquipSlot);
        }
        #endregion


        private string _portal_path_to_string(WoWPoint from, WoWPoint to, List<_hearthomatic_port_location> portal_path)
        {
            string portal_path_string = "";
            //portal_path_string += string.Format("from({0}) -> ", from.ToString());
            portal_path_string += string.Format("from -> ");

            foreach (_hearthomatic_port_location portal in portal_path)
            {
                portal_path_string += string.Format("portal({0}) -> ", portal.name);
            }

            //portal_path_string += string.Format("to({0}) -> ", to.ToString());
            portal_path_string += string.Format("to");

            return portal_path_string;
        }
        //had problems cloning the portal path by properly, something to do with the reference
        private List<_hearthomatic_port_location> _portal_path_clone(List<_hearthomatic_port_location> portal_path)
        {
            List<_hearthomatic_port_location> new_portal_path = new List<_hearthomatic_port_location>();
            foreach (_hearthomatic_port_location portal in portal_path)
            {
                new_portal_path.Add(portal);
            }
            return new_portal_path;
        }
        private float _portal_path_get_distance(WoWPoint from, WoWPoint to, List<_hearthomatic_port_location> portal_path, bool use_penalties)
        {
            float distance = 0;

            if (portal_path.Count == 0)
            {
                //Log("_portal_path_get_distance: error path is empty. distance = {0}", from.Distance2D(to));
                return from.Distance2D(to);
            }

            //distance from "from" to the first portal
            if (portal_path.First().entry_continent_id > 0)
            {
                //Log("_portal_path_get_distance: adding {0} for distance from from to {1}", from.Distance2D(portal_path.First().entry_location), portal_path.First().name);
                distance += from.Distance2D(portal_path.First().entry_location);
            }

            _hearthomatic_port_location prev = new _hearthomatic_port_location();
            uint portal_count = 0;
            foreach (_hearthomatic_port_location portal in portal_path)
            {
                //skip the first, its already done above
                if(prev.configured)
                {
                    if (portal.entry_continent_id > 0)
                    {
                        //distance from previous portal to this one
                        //Log("_portal_path_get_distance: adding {0} for distance from {1} to {2}", prev.exit_location.Distance2D(portal.entry_location), prev.name, portal.name);
                        distance += prev.exit_location.Distance2D(portal.entry_location);
                    }
                }
                prev = portal;

                if (use_penalties && portal.penalty != 0)
                {
                    if (portal_count == 0 && Me.ZoneId == portal.entry_zone_id)
                    {
                        //DebugLog("Not applying portal {0}'s penalty of {1} because we are in the same zone_id ({2}) as its entry ({3})", portal.name, portal.penalty, Me.ZoneId, portal.entry_zone_id);
                    }
                    else
                    {
                        //DebugLog("Applying portal {0}'s penalty of {1}", portal.name, portal.penalty);
                        distance += portal.penalty;
                    }
                }

                portal_count++;
            }

            //distance from the last portal to "to"
            //Log("_portal_path_get_distance: adding {0} for distance from {1} to to", prev.exit_location.Distance2D(to), prev.name);
            distance += prev.exit_location.Distance2D(to);

            return distance;
        }
        private float _portal_path_get_distance(WoWPoint from, WoWPoint to, List<_hearthomatic_port_location> portal_path)
        {
            return _portal_path_get_distance(from, to, portal_path, true);
        }
        private string _temp_depth_indent(uint depth)
        {
            string temp = "";
            uint depth_temp = 3 - depth;
            while (depth_temp > 0)
            {
                temp += "  ";
                depth_temp--;
            }
            temp += depth;
            while (depth > 0)
            {
                temp += "  ";
                depth--;
            }
            return temp;
        }
        private void _find_portal_path(uint depth, uint continent_id, WoWPoint from, WoWPoint to, List<_hearthomatic_port_location> port_locations, List<_hearthomatic_port_location> portal_path, ref List<_hearthomatic_port_location> best_portal_path)
        {
            //branch into possible trees until max depth
            //    if location found is suitable return it

            //Log("_find_portal_path(depth:{0}) running ({1})", _temp_depth_indent(depth), _portal_path_to_string(from, to, portal_path));
            
            //is this portal we found a valid exit portal
            if(portal_path.Count > 0 && portal_path.Last().exit_continent_id == continent_id)//its a portal we might take
            {
                //Log("_find_portal_path(depth:{0}) found a valid exit portal (continent {2} == {3}). returning true. ({1})", _temp_depth_indent(depth), _portal_path_to_string(from, to, portal_path), portal_path.Last().exit_continent_id, continent_id);
                if (best_portal_path.Count == 0 || _portal_path_get_distance(from, to, portal_path) < _portal_path_get_distance(from, to, best_portal_path))
                {
                    //DebugLog("_find_portal_path(depth:{0}) found new best portal path {1}({2}) vs {3}({4}) - {5}", _temp_depth_indent(depth), _portal_path_get_distance(from, to, portal_path), portal_path.Count, _portal_path_get_distance(from, to, best_portal_path), best_portal_path.Count, _portal_path_to_string(from, to, portal_path));
                    DebugLog("_find_portal_path(depth:{0}) found new best portal path {1} yards - {2}", _temp_depth_indent(depth), _portal_path_get_distance(from, to, portal_path), _portal_path_to_string(from, to, portal_path));
                    //best_portal_path = portal_path.ToList();
                    best_portal_path = _portal_path_clone(portal_path);
                }
                else
                {
                    //Log("_find_portal_path(depth:{0}) path is valid, but not as good as best ({1} vs {2})", _temp_depth_indent(depth), _portal_path_get_distance(from, to, portal_path), _portal_path_get_distance(from, to, best_portal_path));
                }
                //return true;
            }


            if (depth <= 1)
            {
                return;
            }
            else
            {
                foreach (_hearthomatic_port_location portal in port_locations)
                {
                    if (
                        portal.ready() &&
                        (portal_path.Count <= 0 && (portal.entry_continent_id <= 0 || portal.entry_continent_id == continent_id)) ||        //our first portal in path, check is it on the same continent
                        (portal_path.Count > 0 && (portal.entry_continent_id > 0 && portal_path.Last().exit_continent_id == portal.entry_continent_id && portal_path.Last().name != portal.name))    //we're investigating a secondary portal
                    )
                    {
                        List<_hearthomatic_port_location> portal_path_temp = portal_path.ToList();
                        portal_path_temp.Add(portal);

                        //Log("_find_portal_path(depth:{0}) checking if path is valid - {1}", _temp_depth_indent(depth), _portal_path_to_string(from, to, portal_path_temp));

                        _find_portal_path(depth - 1, continent_id, from, to, port_locations, portal_path_temp, ref best_portal_path);
                        //Log("");
                    }
                }
            }

            return;
        }
        

        private List<_hearthomatic_port_location> _get_available_portals(LocalPlayer Me)
        {
            List<_hearthomatic_port_location> port_locations = new List<_hearthomatic_port_location>();
            _hearthomatic_port_location portal;

            hearthomatic_status = _hearthomatic_status_NONE;
           
            #region entry and exit paths
            WoWPoint[] stormwind_portal_room_exit_path = new WoWPoint[] { 
                new WoWPoint(-9018.447, 887.8731, 29.6206), 
                new WoWPoint(-9011.675, 867.67, 147.2538), 
                new WoWPoint(-9004.104, 868.0667, 142.9046), 
                new WoWPoint(-9003.69, 876.8965, 138.0133), 
                new WoWPoint(-9012.8, 875.5212, 133.1509), 
                new WoWPoint(-8999.803, 862.4565, 129.4713) 
            };
            WoWPoint[] ironforge_portal_exit_path = new WoWPoint[] { 
                new WoWPoint(-4646.465, -937.6027, 501.6576),
                new WoWPoint(-4737.219, -908.6114, 508.7076),
                new WoWPoint(-4901.106, -897.321, 507.6826),
                new WoWPoint(-4927.882, -854.8589, 506.2563),
                new WoWPoint(-4976.967, -886.2001, 506.9992),
                new WoWPoint(-5050.586, -798.6909, 498.7606)
            };  

            WoWPoint[] orgrimmar_portal_room_exit_path = new WoWPoint[] { 
                new WoWPoint(1771.753, -4328.003, 17.72168), 
                new WoWPoint(1728.147, -4280.142, 23.55666), 
                new WoWPoint(1725.075, -4272.879, 24.31613), 
                new WoWPoint(1710.142, -4289.579, 32.15226),
                new WoWPoint(1711.576, -4324.221, 33.34835)
            };

            WoWPoint[] dalaran_portal_room_exit_path = new WoWPoint[] { 
                new WoWPoint(5801.04, 632.4955, 647.4816)
            };
            #endregion

            #region hearthstones
            //hearthstone
            switch (Me.HearthstoneAreaId)
            {
                case 5150:
                    //stormwind dwarven district hearthstone
                    portal = new _hearthomatic_port_location();
                    portal.name = "Hearthstone (Stormwind Dwarven District)";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_HEARTHSTONE;
                    portal.alliance = true;//not used, but what the hey
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 1519;
                    portal.exit_location = new WoWPoint(-8365.206, 593.0991, 96.99927);
                    portal.exit_path = new WoWPoint[] { new WoWPoint(-8373.439, 596.8099, 97.779), new WoWPoint(-8374.686, 617.8071, 95.23696) };
                    portal.configured = true;
                    port_locations.Add(portal);
                    break;
                case 5148:
                    //stormwind trade district hearthstone
                    portal = new _hearthomatic_port_location();
                    portal.name = "Hearthstone (Stormwind Trade District)";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_HEARTHSTONE;
                    portal.alliance = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 1519;
                    portal.exit_location = new WoWPoint(-8868.188, 674.4605, 97.90355);
                    portal.configured = true;
                    port_locations.Add(portal);
                    break;
                case 4805:
                    //desolace karnum's glade hearthstone
                    portal = new _hearthomatic_port_location();
                    portal.name = "Hearthstone (Desolace Karnum's Glade)";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_HEARTHSTONE;
                    portal.alliance = true;
                    portal.exit_continent_id = 13;
                    portal.exit_zone_id = 405;
                    portal.exit_location = new WoWPoint(-1047.673, 1684.036, 60.13891);
                    portal.configured = true;
                    portal.entry = 43872;
                    port_locations.Add(portal);
                    break;
                /*case 1497:
                    //undercity trade quarter - untesed. I expect problems with escape path, like ironforge.
                    portal = new _hearthomatic_port_location();
                    portal.name = "Hearthstone (undercity trade quarter)";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_HEARTHSTONE;
                    portal.horde = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 1497;
                    portal.exit_location = new WoWPoint(1632.8, 225.2, -43.11);
                    portal.configured = true;
                    port_locations.Add(portal);
                    break;*/
                case 5170:
                    //orgrimmar valley of strength
                    portal = new _hearthomatic_port_location();
                    portal.name = "Hearthstone (Orgrimmar Valley of Strength)";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_HEARTHSTONE;
                    portal.horde = true;
                    portal.exit_continent_id = 13;
                    portal.exit_zone_id = 1637;
                    portal.exit_location = new WoWPoint(1573.65, -4440.411, 16.05529);
                    portal.configured = true;
                    port_locations.Add(portal);
                    break;
                case 1637:
                    //Orgrimmar Valley of Wisdom Hearth
                    portal = new _hearthomatic_port_location();
                    portal.name = "Hearthstone (Orgrimmar Valley of Wisdom)";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_HEARTHSTONE;
                    portal.horde = true;
                    portal.exit_continent_id = 13;
                    portal.exit_zone_id = 1637;
                    portal.exit_location = new WoWPoint(1922.472, -4182.05, 37.88454);
                    portal.configured = true;
                    port_locations.Add(portal);
                    break;
                default:
                     Log("Error! your hearthstone location was not recognized! (areaid = {0})", Me.HearthstoneAreaId);
                    _get_hearthstone_details(port_locations);
                    break;
            }

            #endregion
            #region world portals opened via quests (hyjal, uldum, deepholm, etc)
            foreach (uint id in ObjectManager.Me.QuestLog.GetCompletedQuests())
            {

                if (id == 27545)//alliance user has completed twilight highlands portal quest
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Stormwind -> Twilight Highlands Portal";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                    portal.alliance = true;
                    portal.entry_continent_id = 14;
                    portal.entry_zone_id = 1519;
                    portal.entry_location = new WoWPoint(-8186.939, 414.1919, 117.0084);
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 4922;
                    portal.exit_location = new WoWPoint(-4890.07, -6626.74, 10.2846);
                    portal.configured = true;
                    port_locations.Add(portal);

                    portal = new _hearthomatic_port_location();
                    portal.name = "Twilight Highlands -> Stormwind Portal";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                    portal.alliance = true;
                    portal.entry_continent_id = 14;
                    portal.entry_zone_id = 4922;
                    //portal.entry_location = new WoWPoint(-4893.11, -6623.21, 10.8372);//the actual location. using entry_path now cause flightzor cant get to it
                    portal.entry_location = new WoWPoint(-4884.249, -6633.278, 9.819877);
                    portal.entry_path = new WoWPoint[] { new WoWPoint(-4893.11, -6623.21, 10.8372) };
                    portal.entry_noclicky = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 1519;
                    portal.exit_location = new WoWPoint(-8190.56, 416.829, 116.856);
                    portal.configured = true;
                    port_locations.Add(portal);
                }
                else if (id == 26830)//horde user has completed twilight highlands portal quest
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Orgrimmar -> Twilight Highlands";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                    portal.horde = true;
                    portal.entry_continent_id = 13;
                    portal.entry_zone_id = 1637;
                    portal.entry_location = new WoWPoint(2029.233, -4379.827, 97.9757);
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 4922;
                    portal.exit_location = new WoWPoint(-4032.79, -6318.53, 38.69545);
                    portal.configured = true;
                    port_locations.Add(portal);

                    portal = new _hearthomatic_port_location();
                    portal.name = "Twilight Highlands -> Orgrimmar";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                    portal.horde = true;
                    portal.entry_continent_id = 14;
                    portal.entry_zone_id = 4922;
                    portal.entry_location = new WoWPoint(-4038.4, -6314.7, 38.7043);
                    portal.exit_continent_id = 13;
                    portal.exit_zone_id = 1637;
                    portal.exit_location = new WoWPoint(2047.33, -4378.39, 99.0858);
                    portal.configured = true;
                    portal.penalty = 50;
                    port_locations.Add(portal);
                }



                else if (id == 28112)//user has completed the uldum portal quest, saome quest for alliance and horde
                {
                    if (Me.IsAlliance)
                    {
                        portal = new _hearthomatic_port_location();
                        portal.name = "Stormwind -> Uldum Portal";
                        portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                        portal.alliance = true;
                        portal.entry_continent_id = 14;
                        portal.entry_zone_id = 1519;
                        portal.entry_location = new WoWPoint(-8231.746, 416.395, 117.9941);
                        portal.exit_continent_id = 13;
                        portal.exit_zone_id = 5034;
                        portal.exit_location = new WoWPoint(-9443.31, -958.365, 111.012);
                        portal.configured = true;
                        port_locations.Add(portal);
                    }
                    else if (Me.IsHorde)
                    {
                        portal = new _hearthomatic_port_location();
                        portal.name = "Orgrimmar -> Uldum Portal";
                        portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                        portal.horde = true;
                        portal.entry_continent_id = 13;
                        portal.entry_zone_id = 1637;
                        portal.entry_location = new WoWPoint(2039.67, -4356.288, 98.00434);
                        portal.exit_continent_id = 13;
                        portal.exit_zone_id = 5034;
                        portal.exit_location = new WoWPoint(-9443.31, -958.365, 111.012);
                        portal.configured = true;
                        port_locations.Add(portal);
                    }
                }



                else if (id == 25316) //user has completed the hyjal portal quest. same quest for alliance and horde
                {
                    if (Me.IsAlliance)
                    {
                        //stormwind city portals
                        portal = new _hearthomatic_port_location();
                        portal.name = "Stormwind -> Hyjal Portal";
                        portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                        portal.alliance = true;
                        portal.entry_continent_id = 14;
                        portal.entry_zone_id = 1519;
                        portal.entry_location = new WoWPoint(-8212.124, 400.5488, 117.4606);
                        portal.exit_continent_id = 13;
                        portal.exit_zone_id = 616;
                        portal.exit_location = new WoWPoint(5534.08, -3624.69, 1567.04);
                        portal.exit_path = new WoWPoint[] { new WoWPoint(5533.687, -3640.597, 1611.467) };//HB navigation sometimes has issues unless we move away from the building a little
                        portal.configured = true;
                        port_locations.Add(portal);

                        portal = new _hearthomatic_port_location();
                        portal.name = "Hyjal -> Stormwind Portal";
                        portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                        portal.alliance = true;
                        portal.entry_continent_id = 13;
                        portal.entry_zone_id = 616;
                        //portal.entry_location = new WoWPoint(5541.624, -3588.157, 1567.944);//the actual portal
                        portal.entry_location = new WoWPoint(5567.691, -3774.967, 1653.331);
                        portal.entry_path = new WoWPoint[] { new WoWPoint(5541.264, -3712.648, 1599.609), new WoWPoint(5547.796, -3643.636, 1566.074), new WoWPoint(5541.624, -3588.157, 1567.944) };
                        portal.exit_continent_id = 14;
                        portal.exit_zone_id = 1519;
                        portal.exit_location = new WoWPoint(-8208.25, 429.244, 118.1104);
                        portal.configured = true;
                        portal.penalty = 50;
                        port_locations.Add(portal);
                    }
                    else if (Me.IsHorde)
                    {
                        portal = new _hearthomatic_port_location();
                        portal.name = "Orgrimmar -> Hyjal Portal";
                        portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                        portal.horde = true;
                        portal.entry_continent_id = 13;
                        portal.entry_zone_id = 1637;
                        portal.entry_location = new WoWPoint(2042.752, -4395.662, 97.93639);
                        portal.exit_continent_id = 13;
                        portal.exit_zone_id = 616;
                        portal.exit_location = new WoWPoint(5534.08, -3624.69, 1567.04);
                        //portal.exit_path = new WoWPoint[] { new WoWPoint(5540.595, -3634.362, 1566.247) };//HB navigation sometimes has issues unless we move away from the building a little
                        portal.exit_path = new WoWPoint[] { new WoWPoint(5533.687, -3640.597, 1611.467) };//HB navigation sometimes has issues unless we move away from the building a little
                        portal.configured = true;
                        port_locations.Add(portal);

                        portal = new _hearthomatic_port_location();
                        portal.name = "Hyjal -> Orgrimmar Portal";
                        portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                        portal.horde = true;
                        portal.entry_continent_id = 13;
                        portal.entry_zone_id = 616;
                        //portal.entry_location = new WoWPoint(5503.812, -3624.667, 1567.542);
                        portal.entry_location = new WoWPoint(5567.691, -3774.967, 1653.331);
                        portal.entry_path = new WoWPoint[] { new WoWPoint(5541.264, -3712.648, 1599.609), new WoWPoint(5547.796, -3643.636, 1566.074), new WoWPoint(5503.812, -3624.667, 1567.542) };                       
                        portal.exit_continent_id = 13;
                        portal.exit_zone_id = 1637;
                        portal.exit_location = new WoWPoint(2049.2, -4376.8, 98.8446);
                        portal.configured = true;
                        portal.penalty = 50;
                        port_locations.Add(portal);
                    }
                }


                else if (id == 27727) //user has completed the deepholm portal quest
                {
                    //stormwind city portals
                    portal = new _hearthomatic_port_location();
                    portal.name = "Stormwind -> Deepholm Portal";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                    portal.alliance = true;
                    portal.entry_continent_id = 14;
                    portal.entry_zone_id = 1519;
                    portal.entry_location = new WoWPoint(-8222.979, 450.4059, 117.54);
                    portal.exit_continent_id = 751;
                    portal.exit_zone_id = 5042;
                    portal.exit_location = new WoWPoint(974.921, 566.768, -47.5163);
                    portal.configured = true;
                    port_locations.Add(portal);

                    portal = new _hearthomatic_port_location();
                    portal.name = "Deepholm -> Stormwind Portal";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                    portal.alliance = true;
                    portal.entry_continent_id = 751;
                    portal.entry_zone_id = 5042;
                    portal.entry_location = new WoWPoint(965.1756, 577.1597, -44.25814);
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 1519;
                    portal.exit_location = new WoWPoint(-8208.25, 429.244, 118.1104);
                    portal.configured = true;
                    portal.penalty = 50;
                    port_locations.Add(portal);
                    
                }
                else if (id == 27722)//user has completed the deepholm portal quest
                {
                    Log("todo: horde deepholm portals");
                }


                else if (id == 26971)//the last quest in the therazane quest line (opens portal also)
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Deepholm -> Therazane Portal";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                    portal.alliance = true;
                    portal.horde = true;
                    portal.entry_continent_id = 751;
                    portal.entry_zone_id = 5042;
                    portal.entry_location = new WoWPoint(1084.674, 539.3985, -43.91876);
                    portal.exit_continent_id = 751;
                    portal.exit_zone_id = 5042;
                    portal.exit_location = new WoWPoint(2338.16, 143.69, 179.19);
                    portal.configured = true;
                    port_locations.Add(portal);

                    portal = new _hearthomatic_port_location();
                    portal.name = "Therazane -> Deepholm Portal";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                    portal.alliance = true;
                    portal.horde = true;
                    portal.entry_continent_id = 751;
                    portal.entry_zone_id = 5042;
                    portal.entry_location = new WoWPoint(2335.636, 133.822, 178.9079);
                    portal.exit_continent_id = 751;
                    portal.exit_zone_id = 5042;
                    portal.exit_location = new WoWPoint(1076.78, 537.665, -44.5984);
                    portal.configured = true;
                    port_locations.Add(portal);
                }
            
            }
            #endregion
            #region world portals always available to everyone
            if (Me.IsAlliance)
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Stormwind -> Blasted Lands Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.alliance = true;
                portal.entry_continent_id = 14;
                portal.entry_zone_id = 1519;
                //portal.entry_location = new WoWPoint(-9006.641, 870.2966, 129.6928);//the actual location. using entry_path now cause flightzor cant get to it
                portal.entry_location = new WoWPoint(-8948.475, 818.0017, 193.9883);
                //portal.entry_path = new WoWPoint[] { new WoWPoint(-8994.948, 860.4911, 127.7971), new WoWPoint(-9006.641, 870.2966, 129.6928) };
                portal.entry_path = new WoWPoint[] { new WoWPoint(-9000.397, 862.5482, 129.578), new WoWPoint(-9006.641, 870.2966, 129.6928) };
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 4;
                portal.exit_location = new WoWPoint(-11708.4, -3168, -5.07);
                portal.configured = true;
                port_locations.Add(portal);

                /*portal = new _hearthomatic_port_location();
                portal.name = "Shattrath -> Isle of Quel'Danas";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.alliance = true;
                portal.entry_continent_id = 466;
                portal.entry_zone_id = 3703;
                portal.entry_location = new WoWPoint(-1840.036, 5500.401, -12.42808);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 4080;
                portal.exit_location = new WoWPoint(12803.7, -6907.38, 41.1164);
                portal.ready = true;
                port_locations.Add(portal);*/

                portal = new _hearthomatic_port_location();
                portal.name = "Shattrath -> Stormwind";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.alliance = true;
                portal.entry_continent_id = 466;
                portal.entry_zone_id = 3703;
                portal.entry_location = new WoWPoint(-1893.939, 5388.387, -12.4263);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 1519;
                portal.exit_location = new WoWPoint(-8998.14, 861.254, 29.6206);
                portal.exit_path = stormwind_portal_room_exit_path;        
                portal.configured = true;
                port_locations.Add(portal);

                portal = new _hearthomatic_port_location();
                portal.name = "Dalaran -> Stormwind Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.alliance = true;
                portal.entry_continent_id = 485;
                portal.entry_zone_id = 4395;
                portal.entry_location = new WoWPoint(5719.137, 719.8112, 641.7296);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 1519;
                portal.exit_location = new WoWPoint(-8998.14, 861.254, 29.6206);
                portal.exit_path = stormwind_portal_room_exit_path;
                portal.configured = true;
                port_locations.Add(portal);

                portal = new _hearthomatic_port_location();
                portal.name = "Tol Barad -> Stormwind Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.alliance = true;
                portal.entry_continent_id = 14;
                portal.entry_zone_id = 5389;
                portal.entry_location = new WoWPoint(-343.8437, 1030.016, 22.28569);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 1519;
                portal.exit_location = new WoWPoint(-8208.551, 450.9212, 117.6593);
                portal.configured = true;
                port_locations.Add(portal);
               
                //quite pointless for archaeology, perhaps useful to someone else
                /*portal = new _hearthomatic_port_location();
                portal.name = "Stormwind -> Tol Barad Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.alliance = true;
                portal.entry_continent_id = 14;
                portal.entry_zone_id = 1519;
                portal.entry_location = new WoWPoint(-8208.551, 450.9212, 117.6593);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 5389;
                portal.exit_location = new WoWPoint(-343.8437, 1030.016, 22.28569);
                portal.configured = true;
                port_locations.Add(portal);*/
                
            }
            else if (Me.IsHorde)
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Orgrimmar -> Blasted Lands Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.horde = true;
                portal.entry_continent_id = 13;
                portal.entry_zone_id = 1637;
                portal.entry_location = new WoWPoint(1768.45, -4325.86, -8.3711);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 4;
                portal.exit_location = new WoWPoint(-11708.4, -3168, -5.07);
                portal.configured = true;
                port_locations.Add(portal);

                portal = new _hearthomatic_port_location();
                portal.name = "Thunder Bluff -> Blasted Lands Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.horde = true;
                portal.entry_continent_id = 13;
                portal.entry_zone_id = 1638;
                portal.entry_location = new WoWPoint(-944.0643, 274.8594, 111.71);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 4;
                portal.exit_location = new WoWPoint(-11708.4, -3168, -5.07);
                portal.configured = true;
                port_locations.Add(portal);

                portal = new _hearthomatic_port_location();
                portal.name = "Undercity -> Blasted Lands Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.horde = true;
                portal.entry_continent_id = 14;
                portal.entry_zone_id = 1497;
                portal.entry_location = new WoWPoint(1768.77, 55.3698, -46.3194);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 4;
                portal.exit_location = new WoWPoint(-11708.4, -3168, -5.07);
                portal.configured = true;
                port_locations.Add(portal);

                /*portal = new _hearthomatic_port_location();
                portal.name = "Horde Shattrath -> Isle of Quel'Danas";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.horde = true;
                portal.entry_continent_id = 466;
                portal.entry_zone_id = 3703;
                portal.entry_location = new WoWPoint(-1839.877, 5500.596, -12.42793);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 4080;
                portal.exit_location = new WoWPoint(12803.7, -6907.38, 41.1164);
                portal.ready = true;
                port_locations.Add(portal);*/

                portal = new _hearthomatic_port_location();
                portal.name = "Shattrath -> Orgrimmar";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.horde = true;
                portal.entry_continent_id = 466;
                portal.entry_zone_id = 3703;
                portal.entry_location = new WoWPoint(-1899.45, 5393.01, -12.42832);
                portal.exit_continent_id = 13;
                portal.exit_zone_id = 1637;
                portal.exit_location = new WoWPoint(1776.5, -4338.8, -7.508744);
                portal.exit_path = orgrimmar_portal_room_exit_path;
                portal.configured = true;
                port_locations.Add(portal);

                portal = new _hearthomatic_port_location();
                portal.name = "Dalaran -> Orgrimmar";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.horde = true;
                portal.entry_continent_id = 485;
                portal.entry_zone_id = 4395;
                portal.entry_location = new WoWPoint(5925.874, 593.1875, 640.5933);
                portal.exit_continent_id = 13;
                portal.exit_zone_id = 1637;
                portal.exit_location = new WoWPoint(1776.5, -4338.8, -7.508744);
                portal.exit_path = orgrimmar_portal_room_exit_path;
                portal.configured = true;
                port_locations.Add(portal);

                portal = new _hearthomatic_port_location();
                portal.name = "Tol Barad -> Orgrimmar Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.horde = true;
                portal.entry_continent_id = 14;
                portal.entry_zone_id = 5389;
                portal.entry_location = new WoWPoint(-598.7656, 1377.974, 21.91898);
                portal.exit_continent_id = 13;
                portal.exit_zone_id = 1637;
                portal.exit_location = new WoWPoint(2031.22, -4330.87, 95.471);
                portal.configured = true;
                port_locations.Add(portal);

                //quite pointless for archaeology, perhaps useful to someone else
                /*portal = new _hearthomatic_port_location();
                portal.name = "Orgrimmar -> Tol Barad Portal";
                portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
                portal.horde = true;
                portal.entry_continent_id = 13;
                portal.entry_zone_id = 1637;
                portal.entry_location = new WoWPoint(2031.22, -4330.87, 95.471);
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 5389;
                portal.exit_location = new WoWPoint(-598.7656, 1377.974, 21.91898);
                portal.configured = true;
                port_locations.Add(portal);*/
            }

            portal = new _hearthomatic_port_location();
            portal.name = "Dalaran -> Caverns of Time";
            portal.type_id = _hearthomatic_port_location._port_locations_type_WORLDPORTAL;
            portal.horde = true;
            portal.entry_continent_id = 485;
            portal.entry_zone_id = 4395;
            //portal.entry_location = new WoWPoint(5782.123, 841.0673, 680.3802); //the actual portal location
            //portal.entry_location = new WoWPoint(5812.589, 637.4417, 784.832);  //high up in the sky (fly)
            portal.entry_location = new WoWPoint(5794.785, 766.3829, 665.2424); //entryway (fly)
            portal.entry_path = new WoWPoint[] { 
            //    new WoWPoint(5794.785, 766.3829, 665.2424), //entryway (fly)
                new WoWPoint(5798.565, 839.5297, 680.0857), //top of the stairs (ground)
                new WoWPoint(5782.431, 841.2117, 680.3804)  //the portal (ground)
            };
            portal.exit_continent_id = 13;
            portal.exit_zone_id = 440;
            portal.exit_location = new WoWPoint(-8164.8, -4768.5, 34.3);
            portal.configured = true;
            port_locations.Add(portal);
            
            #endregion
            #region druid only portals
            if (Styx.Logic.Combat.SpellManager.HasSpell(18960))
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Teleport: Moonglade";
                portal.type_id = _hearthomatic_port_location._port_locations_type_SPELL;
                portal.entry = 18960;
                portal.alliance = true;
                portal.exit_continent_id = 13;
                portal.exit_zone_id = 493;
                portal.exit_location = new WoWPoint(7992.238, -2679.25, 512.1005);
                portal.configured = true;
                port_locations.Add(portal);
                
            }
            #endregion
            #region mage only portals
            if (Me.Class == Styx.Combat.CombatRoutine.WoWClass.Mage)
            {
                if (Styx.Logic.Combat.SpellManager.HasSpell(3562))
                {
                    //need to build flying onto my exit path to get out of this one
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Ironforge";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.alliance = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 1537;
                    portal.exit_location = new WoWPoint(-4613.71,-915.287,501.062);
                    portal.exit_path = ironforge_portal_exit_path;
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(3561))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Stormwind";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.alliance = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 1519;
                    portal.exit_location = new WoWPoint(-8998.14, 861.254, 29.6206);
                    portal.exit_path = stormwind_portal_room_exit_path;
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(32271))
                {
                    //teleport exodar
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(3565))
                {
                    // dont use, HB bugs out and cannot get off of teldrasil (probably because its ocean all the way around)
                    /*portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Darnassus";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.alliance = true;
                    portal.exit_continent_id = 13;
                    portal.exit_zone_id = 1657;
                    portal.exit_location = new WoWPoint(9655.368, 2515.18, 1331.64);
                    portal.penalty = 100;
                    portal.ready = true;
                    port_locations.Add(portal);*/
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(3567))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Orgrimmar";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.horde = true;
                    portal.exit_continent_id = 13;
                    portal.exit_zone_id = 1637;
                    portal.exit_location = new WoWPoint(1776.5, -4338.8, -7.508744);
                    portal.exit_path = orgrimmar_portal_room_exit_path;
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(3563))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Undercity";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.horde = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 1497;
                    portal.exit_location = new WoWPoint(1773.42, 61.7391, -46.3215);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(32272))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Silvermoon";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.horde = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 3487;
                    portal.exit_location = new WoWPoint(9998.15, -7106.322, 47.7054);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(3566))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Thunder Bluff";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.horde = true;
                    portal.exit_continent_id = 13;
                    portal.exit_zone_id = 1638;
                    portal.exit_location = new WoWPoint(-967.375, 284.82, 110.773);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(49358))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Stonard";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.horde = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 8;
                    portal.exit_location = new WoWPoint(-10469, -3331.54, 25.4716);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(49359))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Theramore";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.alliance = true;
                    portal.exit_continent_id = 13;
                    portal.exit_zone_id = 15;
                    portal.exit_location = new WoWPoint(-3748.11, -4440.21, 30.55237);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(33690))//alliance shattrath
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Shattrath";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.alliance = true;
                    portal.exit_continent_id = 466;
                    portal.exit_zone_id = 3703;
                    portal.exit_location = new WoWPoint(-1824.32, 5417.23, -12.4277);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }

                if (Styx.Logic.Combat.SpellManager.HasSpell(35715))//horde shattrath
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Shattrath";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.horde = true;
                    portal.exit_continent_id = 466;
                    portal.exit_zone_id = 3703;
                    portal.exit_location = new WoWPoint(-1902.49, 5442.86, -12.428);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }


                if (Styx.Logic.Combat.SpellManager.HasSpell(53140))
                {
                    //teleport dalarn
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Dalaran";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.alliance = true;
                    portal.horde = true;
                    portal.exit_continent_id = 485;
                    portal.exit_zone_id = 4395;
                    portal.exit_location = new WoWPoint(5807.75, 588.347, 660.9391);
                    portal.exit_path = dalaran_portal_room_exit_path;
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }

                if (Styx.Logic.Combat.SpellManager.HasSpell(88342))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Tol Barad";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.alliance = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 5389;
                    portal.exit_location = new WoWPoint(-369.208, 1058.73, 21.7719);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }

                if (Styx.Logic.Combat.SpellManager.HasSpell(88344))
                {
                    portal = new _hearthomatic_port_location();
                    portal.name = "Teleport: Tol Barad";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_MAGEPORTAL;
                    portal.horde = true;
                    portal.exit_continent_id = 14;
                    portal.exit_zone_id = 5389;
                    portal.exit_location = new WoWPoint(-598.7656, 1377.974, 21.91898);
                    portal.penalty = 100;
                    portal.configured = true;
                    port_locations.Add(portal);
                }
                
            }
            #endregion
            #region item portals
            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 63352) != null)//alliance Shroud of Cooperation
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Shroud of Cooperation (Stormwind)";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 1519;
                portal.exit_location = new WoWPoint(-8998.14, 861.254, 29.6206);
                portal.exit_path = stormwind_portal_room_exit_path;
                portal.entry = 63352;
                portal.configured = true;
                port_locations.Add(portal);
            }
            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 63206) != null)//alliance Wrap of Unity
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Wrap of Unity (Stormwind)";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 1519;
                portal.exit_location = new WoWPoint(-8998.14, 861.254, 29.6206);
                portal.exit_path = stormwind_portal_room_exit_path;
                portal.entry = 63206;
                portal.configured = true;
                port_locations.Add(portal);
            }
            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 65360) != null)//alliance Cloak of Coordination
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Cloak of Coordination (Stormwind)";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 1519;
                portal.exit_location = new WoWPoint(-8998.14, 861.254, 29.6206);
                portal.exit_path = stormwind_portal_room_exit_path;
                portal.entry = 65360;
                portal.configured = true;
                port_locations.Add(portal);
            }
            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 63353) != null)//horde Shroud of Cooperation
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Shroud of Cooperation (Orgrimmar)";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.horde = true;
                portal.exit_continent_id = 13;
                portal.exit_zone_id = 1637;
                portal.exit_location = new WoWPoint(1776.5, -4338.8, -7.508744);
                portal.exit_path = orgrimmar_portal_room_exit_path;
                portal.entry = 63353;
                portal.configured = true;
                port_locations.Add(portal);
            }
            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 63207) != null)//horde wrap of unity
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Wrap of Unity (Orgrimmar)";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.horde = true;
                portal.exit_continent_id = 13;
                portal.exit_zone_id = 1637;
                portal.exit_location = new WoWPoint(1776.5, -4338.8, -7.508744);
                portal.exit_path = orgrimmar_portal_room_exit_path;
                portal.entry = 63207;
                portal.configured = true;
                port_locations.Add(portal);
            }
            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 65274) != null)//horde Cloak of Coordination
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Cloak of Coordination (Orgrimmar)";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.horde = true;
                portal.exit_continent_id = 13;
                portal.exit_zone_id = 1637;
                portal.exit_location = new WoWPoint(1776.5, -4338.8, -7.508744);
                portal.exit_path = orgrimmar_portal_room_exit_path;
                portal.entry = 65274;
                portal.configured = true;
                port_locations.Add(portal);
            }


            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 58487) != null)//potion of deepholm
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Potion of Deepholm";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.horde = true;
                portal.exit_continent_id = 751;
                portal.exit_zone_id = 5042;
                portal.exit_location = new WoWPoint(909.976, 502.649, -49.2183);
                portal.entry = 58487;
                portal.penalty = 4000;//probably want a pretty hefty penalty for using this item
                portal.configured = true;
                port_locations.Add(portal);
            }


            //Ring of the Kirin Tor
            WoWItem item = ObjectManager.Me.CarriedItems.FirstOrDefault(x => 
                x.Entry == 40585 || //http://www.wowhead.com/item=40585
                x.Entry == 40586 || //http://www.wowhead.com/item=40586
                x.Entry == 44934 || //http://www.wowhead.com/item=44934
                x.Entry == 44935 || //http://www.wowhead.com/item=44935
                x.Entry == 45688 || //http://www.wowhead.com/item=45688
                x.Entry == 45689 || //http://www.wowhead.com/item=45689
                x.Entry == 45690 || //http://www.wowhead.com/item=45690
                x.Entry == 45691 || //http://www.wowhead.com/item=45691
                x.Entry == 48954 || //http://www.wowhead.com/item=48954
                x.Entry == 48955 || //http://www.wowhead.com/item=48955
                x.Entry == 48956 || //http://www.wowhead.com/item=48956
                x.Entry == 48957 || //http://www.wowhead.com/item=48957
                x.Entry == 51557 || //http://www.wowhead.com/item=51557
                x.Entry == 51558 || //http://www.wowhead.com/item=51558
                x.Entry == 51559 || //http://www.wowhead.com/item=51559
                x.Entry == 51560    //http://www.wowhead.com/item=51560            
               );
            if (item != null)//Ring of the Kirin Tor
            {
                portal = new _hearthomatic_port_location();
                portal.name = item.Name;
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.horde = true;
                portal.exit_continent_id = 485;
                portal.exit_zone_id = 4395;
                portal.exit_location = new WoWPoint(5807.75, 588.347, 660.9391);
                portal.exit_path = dalaran_portal_room_exit_path;
                portal.entry = item.Entry;
                portal.configured = true;
                port_locations.Add(portal);
            }

            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 63379) != null)//Baradin's Wardens Tabard
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Baradin's Wardens Tabard";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 5389;
                portal.exit_location = new WoWPoint(-369.208, 1058.73, 21.7719);
                portal.entry = 63379;
                portal.configured = true;
                port_locations.Add(portal);
            }

            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 63378) != null)//Hellscream's Reach Tabard
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Hellscream's Reach Tabard";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.horde = true;
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 5389;
                portal.exit_location = new WoWPoint(-598.7656, 1377.974, 21.91898);
                portal.entry = 63378;
                portal.configured = true;
                port_locations.Add(portal);
            }

            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 46874) != null)//Argent Crusader's Tabard
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Argent Crusader's Tabard";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.horde = true;
                portal.exit_continent_id = 485;
                portal.exit_zone_id = 210;
                portal.exit_location = new WoWPoint(8480.55, 1092.9, 554.485);
                portal.entry = 46874;
                portal.configured = true;
                port_locations.Add(portal);
            }

            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 52251) != null)//Jaina's Locket
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Jaina's Locket";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.horde = true;
                portal.exit_continent_id = 485;
                portal.exit_zone_id = 4395;
                portal.exit_location = new WoWPoint(5807.75, 588.347, 660.9391);
                portal.exit_path = dalaran_portal_room_exit_path;
                portal.entry = 52251;
                portal.configured = true;
                port_locations.Add(portal);
            }

            if (ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 50287) != null)//Boots of the Bay
            {
                portal = new _hearthomatic_port_location();
                portal.name = "Boots of the Bay";
                portal.type_id = _hearthomatic_port_location._port_locations_type_ITEM;
                portal.alliance = true;
                portal.horde = true;
                portal.exit_continent_id = 14;
                portal.exit_zone_id = 5287;
                portal.exit_location = new WoWPoint(-14459.76, 493.0189, 15.12456);
                portal.entry = 50287;
                portal.configured = true;
                port_locations.Add(portal);
            }

            

            #endregion

            return port_locations;
        }

        private string _http_get(string url)
        {
            byte[] buf = new byte[8192];
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            // we will read data via the response stream
            Stream resStream = response.GetResponseStream();

            string results = "";
            int read = 0;
            do
            {
                read = resStream.Read(buf, 0, buf.Length);

                if (read != 0)
                {
                    // translate from bytes to ASCII text (isnt http raw data ascii anyway?!, silly c#)
                    results += System.Text.Encoding.ASCII.GetString(buf, 0, read);
                }
            }
            while (read > 0);

            return results;
        }


        #region unknown hearthstone functions
        private void _get_hearthstone_details(List<_hearthomatic_port_location> port_locations)
        {
            DebugLog("- checking with website for updated hearth location");
            string results = _http_get(string.Format("http://hearthomatic.andyt.co/hearthomatic.php?function=get_hearthstone&HearthstoneAreaId={0}", Me.HearthstoneAreaId));
            bool success = false;
            if (results.Contains("result=1"))
            {
                //lots of string stuff here that could go wrong, just catch the whole lot
                try
                {
                    int status = 0;

                    _hearthomatic_port_location portal = new _hearthomatic_port_location();
                    portal.name = "Hearthstone";
                    portal.type_id = _hearthomatic_port_location._port_locations_type_HEARTHSTONE;

                    foreach (string temp in results.Split('&'))
                    {
                        string[] temp2 = temp.Split('=');

                        string key = temp2[0];//just to make my brain hurt less
                        string value = temp2[1];

                        if (key == "exit_continent_id" && uint.Parse(value) > 0)
                        {
                            portal.exit_continent_id = uint.Parse(value);
                            status++;
                        }
                        else if (key == "exit_zone_id" && uint.Parse(value) > 0)
                        {
                            portal.exit_zone_id = uint.Parse(value);
                            status++;
                        }
                        else if (key == "exit_location")
                        {
                            value = value.Remove(0, 1);
                            value = value.Remove(value.Length - 1, 1);
                            string[] temp3 = value.Split(',');
                            if (temp3.Length == 3 && float.Parse(temp3[0]) != 0 && float.Parse(temp3[1]) != 0 && float.Parse(temp3[2]) != 0)
                            {
                                portal.exit_location = new WoWPoint(float.Parse(temp3[0]), float.Parse(temp3[1]), float.Parse(temp3[2]));
                                status++;
                            }
                        }
                        else if (key == "zonetext")//optional
                        {
                            portal.name += " " + value;
                        }
                        else if (key == "GetBindLocation")//optional
                        {
                            portal.name += " " + value;
                        }
                        else if (key == "horde" && uint.Parse(value) == 1)
                        {
                            portal.horde = true;
                            status++;
                        }
                        else if (key == "alliance" && uint.Parse(value) == 1)
                        {
                            portal.alliance = true;
                            status++;
                        }
                        else if (key == "inkeeper_id" && uint.Parse(value) > 0)//optional
                        {
                            portal.entry = uint.Parse(value);
                        }

                    }

                    if (status >= 4)
                    {
                        portal.configured = true;
                        port_locations.Add(portal);

                        success = true;
                        Log("- successfully configured your hearthstone with details from website. {6} area id={0}, exit_continent_id={1}, exit_zone_id={2}, exit_location={3},{4},{5}", Me.HearthstoneAreaId, portal.exit_continent_id, portal.exit_zone_id, portal.exit_location.X, portal.exit_location.Y, portal.exit_location.Z, portal.name);
                    }
                }
                catch (Exception e)
                {
                    DebugLog("- error processing hearthstone info - {0} - {1}", results, e);
                }
            }

            if (!success)
            {
                Log("when you start the bot we will use your hearth to work out where it goes. The author will be sent its details so we can add it later.");
                Log("---");
                _submit_hearthstone = true;
            }
        }
        private void _submit_hearthstone_details()
        {
            uint old_zoneid = Me.ZoneId;

            _hearthomatic_port_location portal = new _hearthomatic_port_location();
            portal.name = "Hearthstone";
            portal.type_id = _hearthomatic_port_location._port_locations_type_HEARTHSTONE;
            portal.exit_continent_id = 0;
            portal.exit_zone_id = 0;
            portal.exit_location = new WoWPoint();
            portal.configured = true;

            if (portal.ready())
            {
                int count = 10;
                while (count > 0)
                {
                    Log("going to use your hearthstone in {0}", count);
                    Thread.Sleep(1000);
                    count--;
                }

                if (Flightor.MountHelper.Mounted)
                {
                    DebugLog(string.Format("landing so we can port"));
                    _land();
                    DebugLog(string.Format("dismount"));
                    _dismount();
                }

                uint result = portal.use();
                if (result == _hearthomatic_port_location.use_result_SUCCESS)
                {
                    Thread.Sleep(2000);

                    int tries = 5;
                    while (Me.ZoneId == old_zoneid && tries > 0)
                    {
                        Thread.Sleep(1000);
                        tries--;
                    }
                    if (Me.ZoneId == old_zoneid)
                    {
                        DebugLog("something went wrong using hearthstone");
                    }
                    else
                    {
                        DebugLog("hearthstone successful");

                        string[] t2 = Lua.GetReturnValues("local t = {}; " +
                                                "SetMapToCurrentZone(); " +
                                                "SetMapZoom(GetCurrentMapContinent()); " +
                                                "table.insert(t, GetCurrentMapAreaID()); " +
                                                "table.insert(t, GetBindLocation()); " +
                                                "return unpack(t); ").ToArray();
                        uint current_map_id = uint.Parse(t2[0]);
                        string GetBindLocation = t2[1];
                        DebugLog("current_map_id = {0}", current_map_id);


                        //try to get the closest npc / inkeeper
                        uint inkeeper_id = 0;
                        try
                        {
                            List<WoWObject> objects = ObjectManager.ObjectList.FindAll(item => item.Type == WoWObjectType.Unit && !item.IsMe);
                            while (objects.First().Distance2D <= 0)
                            {
                                DebugLog("waiting for object distance calculations {0}", objects.First().Distance2D);
                                ObjectManager.Update();
                                StyxWoW.SleepForLagDuration();
                                Thread.Sleep(200);
                            }
                            List<WoWObject> objects2 = objects.OrderBy(item => item.Distance2D).ToList();
                            /*foreach (WoWObject _object in objects2)
                            {
                                Log("object: {1}={0}={2}", _object.Name, _object.Entry, _object.Distance2D);
                            }*/
                            if (objects2.Count > 0)
                            {
                                inkeeper_id = objects2.First().Entry;
                                DebugLog("found inkpeeper is probably: id={0} name={1} type={2}", objects2.First().Entry, objects2.First().Name, objects2.First().Type);
                            }
                        }
                        catch (Exception e)
                        {
                            DebugLog("failed locating inkeeper - {0}", e);
                        }

                        //Me.HearthstoneAreaId;
                        portal.exit_continent_id = current_map_id;
                        portal.exit_zone_id = Me.ZoneId;
                        portal.exit_location = Me.Location;
                        portal.name += Me.ZoneText + GetBindLocation;
                        port_locations.Add(portal);

                        DebugLog("submitting your hearthstone");
                        string results = _http_get(string.Format("http://hearthomatic.andyt.co/hearthomatic.php?function=submit_hearthstone&HearthstoneAreaId={0}&exit_continent_id={1}&exit_zone_id={2}&exit_location=({3},{4},{5})&zonetext={6}&GetBindLocation={7}&horde={8}&alliance={9}&inkeeper_id={10}", Me.HearthstoneAreaId, current_map_id, Me.ZoneId, Me.Location.X, Me.Location.Y, Me.Location.Z, Me.ZoneText, GetBindLocation, Me.IsHorde, Me.IsAlliance, inkeeper_id));
                        Log("submitted - {0}", results);
                    }
                }
                else
                {
                    DebugLog("something went wrong using hearthstone");
                }
            }
            else
            {
                DebugLog("your hearthstone is on cooldown. cant work out where it goes. disabling it");
            }

            return;
        }
        #endregion



        public class _hearthomatic_port_location
        {
            public string name;
            public int type_id;//hearthstone, portal, mage portal, item, ... etc

            public bool alliance;
            public bool horde;

            public uint entry;//for items, and perhaps spell id's too. for hearthstones this is used for the id of the inkeeper that is nearby.
            public float penalty;//adds a penalty to distance when considering this portal. used to make some portals less attractive than others when they would normally be considered equal

            public bool configured;//make this true when the portal is configured and available

            public uint entry_continent_id;
            public uint entry_zone_id;
            public WoWPoint entry_location;
            public bool entry_noclicky; //its a portal that just requires walking into it (the one in twilight highlands)
            public WoWPoint[] entry_path;//do we need to follow a path to actually get to portal entry point (this is done after getting to entry_location)

            public uint exit_continent_id;
            public uint exit_zone_id;
            public WoWPoint exit_location;
            public WoWPoint[] exit_path;//do we need to follow a path to leave the portal exit point (this is taken after clicking the portal)

            //public bool test_forcedirect;//have to re-implement this

            private uint combat_fail_attempts;
            private static uint combat_fail_attempts_max = 2;

            public static int _port_locations_type_HEARTHSTONE = 0;
            public static int _port_locations_type_WORLDPORTAL = 1;
            public static int _port_locations_type_MAGEPORTAL = 2;
            public static int _port_locations_type_ITEM = 3;
            public static int _port_locations_type_SPELL = 4;

            public static uint use_result_SUCCESS = 0;
            public static uint use_result_FAIL = 1;
            public static uint use_result_FAILCOMBAT = 2;

            private DateTime ready_cache_time = DateTime.Now.AddSeconds(-60);
            private bool ready_cache_result = false;


            public _hearthomatic_port_location()
            {
                configured = false;
                combat_fail_attempts = 0;
            }

            #region portal functions
            public bool ready()
            {
                if (ready_cache_time != null && DateTime.Now.Subtract(ready_cache_time).TotalSeconds < 30)
                {
                    return ready_cache_result;
                }

                ready_cache_result = ready_real();
                ready_cache_time = DateTime.Now;

                return ready_cache_result;
            }
            private bool ready_real()
            {
                if (type_id == _hearthomatic_port_location._port_locations_type_HEARTHSTONE)
                {
                    WoWSpell astral_recall = WoWSpell.FromId(556);//astral recall. silly shamans
                    if (Styx.Logic.Combat.SpellManager.HasSpell(556) && astral_recall != null && !astral_recall.Cooldown)
                    {
                        return true;
                    }

                    WoWItem hearthstone = ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 6948 || x.Entry == 64488);//hearthstone or inkeepers daugher
                    if (hearthstone != null && hearthstone.Cooldown <= 0)
                    {
                        return true;
                    }                    
                }
                else if (type_id == _hearthomatic_port_location._port_locations_type_WORLDPORTAL)
                {
                    return true;
                }
                else if (type_id == _hearthomatic_port_location._port_locations_type_MAGEPORTAL)
                {
                    //check we have reagents
                    if (Me.Class != Styx.Combat.CombatRoutine.WoWClass.Mage)
                    {
                        return false;
                    }

                    WoWItem reagent = ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 17031);//rune of teleportation
                    if (reagent == null)
                    {
                        return false;
                    }

                    return true;
                }
                else if (type_id == _hearthomatic_port_location._port_locations_type_ITEM && entry > 0)
                {
                    uint id = entry;
                    WoWItem item = ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == id);
                    if (item != null)
                    {
                        if (item.Cooldown <= 0)
                        {
                            return true;
                        }
                    }
                }
                else if (type_id == _hearthomatic_port_location._port_locations_type_SPELL && entry > 0)
                {
                    if (Styx.Logic.Combat.SpellManager.HasSpell((int)entry))
                    {
                        WoWSpell spell = Styx.Logic.Combat.WoWSpell.FromId((int)entry);
                        if (!spell.Cooldown)
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
            public uint use()
            {
                if (ready())
                {
                    uint status = use_result_FAIL;

                    ready_cache_time = DateTime.Now.AddSeconds(-60);//effectively remove any cache

                    if (type_id == _hearthomatic_port_location._port_locations_type_HEARTHSTONE)
                    {
                        int retry = 3;
                        do
                        {
                            WoWSpell astral_recall = WoWSpell.FromId(556);//astral recall. silly shamans think theyre special!
                            WoWItem hearthstone = ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == 6948 || x.Entry == 64488);//hearthstone or inkeepers daugher

                            if (Styx.Logic.Combat.SpellManager.HasSpell(556) && astral_recall != null && !astral_recall.Cooldown)
                            {
                                DebugLog("casting {0}", astral_recall.Name);
                                astral_recall.Cast();
                            }
                            else if (hearthstone != null && hearthstone.Cooldown <= 0)
                            {
                                DebugLog("casting {0}", hearthstone.Name);
                                hearthstone.Use();
                            }

                            StyxWoW.SleepForLagDuration();
                            Thread.Sleep(500);
                            ObjectManager.Update();

                            retry--;
                        }
                        while (!StyxWoW.Me.Combat && !StyxWoW.Me.Dead && !StyxWoW.Me.IsCasting && retry > 0);

                        if (retry <= 0)
                        {
                            Log("error couldnt use hearthstone");
                            if (StyxWoW.Me.Combat)
                            {
                                Styx.Logic.Combat.SpellManager.StopCasting();
                                status = use_result_FAILCOMBAT;
                            }
                            else
                            {
                                status = use_result_FAIL;
                            }                            
                        }
                        else
                        {
                            DebugLog("hearthstone is casting, waiting");
                            status = use_wait_for_cast(15000);
                        }
                    }
                    else if (type_id == _hearthomatic_port_location._port_locations_type_MAGEPORTAL)
                    {
                        //find the spell to match this portal
                        int retry = 3;
                        do
                        {
                            DebugLog("casting {0}", name);

                            Styx.Logic.Combat.SpellManager.Cast(name);

                            StyxWoW.SleepForLagDuration();
                            Thread.Sleep(500);
                            ObjectManager.Update();
                            retry--;
                        }
                        while (!StyxWoW.Me.Combat && !StyxWoW.Me.Dead && !StyxWoW.Me.IsCasting && retry > 0);

                        if (retry <= 0)
                        {
                            Log("error couldnt cast mage portal");
                            if (StyxWoW.Me.Combat)
                            {
                                status = use_result_FAIL;
                            }
                            else
                            {
                                status = use_result_FAILCOMBAT;
                            }
                        }
                        else
                        {
                            DebugLog("{0} is casting, waiting", name);
                            status = use_wait_for_cast(15000);
                        }
                    }
                    else if (type_id == _hearthomatic_port_location._port_locations_type_WORLDPORTAL)
                    {
                        if (!entry_noclicky)
                        {
                            //find a portal near us, and click it. its probably, maybe, likely to be the one we want ;p
                            Thread.Sleep(100);
                            ObjectManager.Update();
                            Thread.Sleep(100);

                            DebugLog("trying to find and click portal {0}", name);
                            List<WoWGameObject> _objectList = ObjectManager.GetObjectsOfType<WoWGameObject>();
                            foreach (WoWGameObject _object in _objectList)
                            {
                                if (_object.SubType == WoWGameObjectType.SpellCaster && _object.Distance < 5)
                                {
                                    WoWMovement.MoveStop();
                                    DebugLog("clicking on portal {0}", _object.ToString());
                                    status = use_result_SUCCESS;
                                    _object.Interact();
                                    Thread.Sleep(10000);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            status = use_wait_for_cast(10000);
                        }

                        if (status != use_result_SUCCESS)
                        {
                            DebugLog("error! couldnt find portal to click!!");
                        }
                    }
                    else if (type_id == _hearthomatic_port_location._port_locations_type_ITEM)
                    {
                        uint id = entry;
                        WoWItem item = ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == id);
                        if (item != null && item.Entry > 0)
                        {
                            int original_slot = (int)item.ItemInfo.EquipSlot;
                            uint original_item = 0;// had to use the id instead, because for some reason HB will crash when trying to use a WoWItem immediately after a loading screen!

                            if (original_slot > 0)
                            {
                                //need to find the item we are wearing currently, so we can swap it back.
                                //  easier said than done
                                List<Styx.Logic.Inventory.InventorySlot> slots = Styx.Logic.Inventory.InventoryManager.GetInventorySlotsByEquipSlot(item.ItemInfo.EquipSlot);
                                foreach (Styx.Logic.Inventory.InventorySlot slot in slots)//i guess this could be multiple (ring1 ring2 perhaps)
                                {
                                    WoWItem[] items = ObjectManager.Me.Inventory.Equipped.Items;
                                    for (int i = 0; i < items.Length; i++)
                                    {
                                        WoWItem item2 = items[i];
                                        if (item2 != null && item2.Entry > 0 && item2.ItemInfo.EquipSlot == item.ItemInfo.EquipSlot)
                                        {
                                            original_item = item2.Entry;
                                            break;
                                        }
                                    }
                                }

                                DebugLog("Equipping item {0}", item.ToString());
                                EquipItem(item);

                                StyxWoW.SleepForLagDuration();
                                Thread.Sleep(500);
                                ObjectManager.Update();

                                while (item.HasEquipCooldown && item.Cooldown > 0)
                                {
                                    DebugLog("waiting for equip cooldown");
                                    ObjectManager.Update();
                                    StyxWoW.SleepForLagDuration();
                                    Thread.Sleep(1000);
                                }

                                if (StyxWoW.Me.Combat)
                                {
                                    return use_result_FAILCOMBAT;
                                }

                                DebugLog("using item {0}", item.ToString());
                                item.Use();

                                status = use_wait_for_cast(10000);

                                if (status == use_result_SUCCESS)
                                {
                                    _wait_for_load();

                                    StyxWoW.SleepForLagDuration();
                                    Thread.Sleep(500);
                                    ObjectManager.Update();

                                    UnequipSlot(original_slot);

                                    if (original_item > 0)//we were wearing something in that slot. swap it back when done. careful tho, sometimes on roleplay servers i don't wear pants
                                    {
                                        WoWItem item2 = ObjectManager.Me.CarriedItems.FirstOrDefault(x => x.Entry == original_item);
                                        if (item2 != null)
                                        {
                                            DebugLog("Equipping original item {0}", original_item);
                                            EquipItem(item2);

                                            StyxWoW.SleepForLagDuration();
                                            Thread.Sleep(500);
                                            ObjectManager.Update();
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else if (type_id == _hearthomatic_port_location._port_locations_type_SPELL)
                    {
                        WoWSpell spell = Styx.Logic.Combat.WoWSpell.FromId((int)entry);

                        int retry = 3;
                        do
                        {
                            spell.Cast();

                            StyxWoW.SleepForLagDuration();
                            Thread.Sleep(500);
                            ObjectManager.Update();

                            retry--;
                        }
                        while (!StyxWoW.Me.Combat && !StyxWoW.Me.IsCasting && retry > 0);

                        if (retry <= 0)
                        {
                            Log("error couldnt use spell");
                            if (StyxWoW.Me.Combat)
                            {
                                status = use_result_FAIL;
                            }
                            else
                            {
                                status = use_result_FAILCOMBAT;
                            }
                        }
                        else
                        {
                            DebugLog("spell is casting, waiting");
                            status = use_wait_for_cast(15000);
                        }
                    }


                    if (status == use_result_FAILCOMBAT && combat_fail_attempts >= combat_fail_attempts_max)
                    {
                        DebugLog("failed {0} times because of combat. reporting critical error", combat_fail_attempts);
                        status = use_result_FAIL;
                    }

                    if (status == use_result_SUCCESS)
                    {
                        combat_fail_attempts = 0;

                        _wait_for_load();

                        ObjectManager.Update();
                        StyxWoW.SleepForLagDuration();
                        Thread.Sleep(200);
                    }
                    else
                    {
                        combat_fail_attempts++;
                    }

                    return status;
                }
                return use_result_FAIL;
            }

            /// <summary>
            /// waits for in_sleep_time, or cancels waiting if in combat.
            /// returns status to be returned by use (use_result_FAILCOMBAT or use_result_SUCCESS).
            /// </summary>
            private uint use_wait_for_cast(int sleep_time)
            {
                while ((sleep_time > 0 || StyxWoW.Me.IsCasting) && !StyxWoW.Me.Combat && !StyxWoW.Me.Dead)
                {
                    sleep_time -= 1000;
                    Thread.Sleep(1000);
                }

                if (StyxWoW.Me.Combat)
                {
                    DebugLog("casting failed because of combat");
                    Styx.Logic.Combat.SpellManager.StopCasting();
                    return use_result_FAILCOMBAT;
                }
                else
                {
                    return use_result_SUCCESS;
                }
            }

            /// <summary>
            /// returns true if you should dismount to use this portal.
            /// (defaults to true if unknown)
            /// </summary>
            public bool needs_dismount()
            {
                //dont dismount for world and nonclicky portals
                if (entry_noclicky || type_id == _port_locations_type_WORLDPORTAL)
                {
                    return false;
                }

                return true;
            }

            /// <summary>
            /// returns true if you should let HB pulse at the various process_status stages of using this portal
            /// (defaults to true if unknown)
            /// </summary>
            public bool needs_pulse(int status)
            {
                if (status == _hearthomatic_status_STEP1)
                {
                    //we cant pull combat for these sort of portals, and even if we did we can still use them
                    if (entry_noclicky || type_id == _port_locations_type_WORLDPORTAL)
                    {
                        return false;
                    }
                }
                else if (status == _hearthomatic_status_STEP2)
                {
                    //step2 uses the portal, so i cant see is ever NOT wanting to pulse after step2
                    // if we dont, the HB navigation system will not work at all and we wont be able to move
                }
                else if (status == _hearthomatic_status_STEP3)
                {
                    //step3 is the post-cleanup after taking the portal, having pulsed and taken the exit path and this function is only called if there IS another portal to take
                    //  come to think on it, when do we actually need a pulse here??
                    return false;
                }

                return true;
            }

            #endregion
        }
    }
}




